Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
c296d5f9 TP |
2 | #include <linux/export.h> |
3 | #include <linux/errno.h> | |
4 | #include <linux/gpio.h> | |
5 | #include <linux/spi/spi.h> | |
c296d5f9 TP |
6 | #include "fbtft.h" |
7 | ||
8 | int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len) | |
9 | { | |
10 | struct spi_transfer t = { | |
11 | .tx_buf = buf, | |
12 | .len = len, | |
13 | }; | |
14 | struct spi_message m; | |
15 | ||
16 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, | |
17 | "%s(len=%d): ", __func__, len); | |
18 | ||
19 | if (!par->spi) { | |
20 | dev_err(par->info->device, | |
21 | "%s: par->spi is unexpectedly NULL\n", __func__); | |
22 | return -1; | |
23 | } | |
24 | ||
25 | spi_message_init(&m); | |
c296d5f9 TP |
26 | spi_message_add_tail(&t, &m); |
27 | return spi_sync(par->spi, &m); | |
28 | } | |
29 | EXPORT_SYMBOL(fbtft_write_spi); | |
30 | ||
31 | /** | |
32 | * fbtft_write_spi_emulate_9() - write SPI emulating 9-bit | |
33 | * @par: Driver data | |
34 | * @buf: Buffer to write | |
35 | * @len: Length of buffer (must be divisible by 8) | |
36 | * | |
37 | * When 9-bit SPI is not available, this function can be used to emulate that. | |
38 | * par->extra must hold a transformation buffer used for transfer. | |
39 | */ | |
40 | int fbtft_write_spi_emulate_9(struct fbtft_par *par, void *buf, size_t len) | |
41 | { | |
42 | u16 *src = buf; | |
43 | u8 *dst = par->extra; | |
44 | size_t size = len / 2; | |
45 | size_t added = 0; | |
46 | int bits, i, j; | |
47 | u64 val, dc, tmp; | |
48 | ||
49 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, | |
50 | "%s(len=%d): ", __func__, len); | |
51 | ||
52 | if (!par->extra) { | |
53 | dev_err(par->info->device, "%s: error: par->extra is NULL\n", | |
54 | __func__); | |
55 | return -EINVAL; | |
56 | } | |
57 | if ((len % 8) != 0) { | |
58 | dev_err(par->info->device, | |
aed1c72e | 59 | "error: len=%zu must be divisible by 8\n", len); |
c296d5f9 TP |
60 | return -EINVAL; |
61 | } | |
62 | ||
63 | for (i = 0; i < size; i += 8) { | |
64 | tmp = 0; | |
65 | bits = 63; | |
66 | for (j = 0; j < 7; j++) { | |
67 | dc = (*src & 0x0100) ? 1 : 0; | |
68 | val = *src & 0x00FF; | |
69 | tmp |= dc << bits; | |
70 | bits -= 8; | |
71 | tmp |= val << bits--; | |
72 | src++; | |
73 | } | |
74 | tmp |= ((*src & 0x0100) ? 1 : 0); | |
caaba677 | 75 | *(__be64 *)dst = cpu_to_be64(tmp); |
c296d5f9 TP |
76 | dst += 8; |
77 | *dst++ = (u8)(*src++ & 0x00FF); | |
78 | added++; | |
79 | } | |
80 | ||
81 | return spi_write(par->spi, par->extra, size + added); | |
82 | } | |
83 | EXPORT_SYMBOL(fbtft_write_spi_emulate_9); | |
84 | ||
85 | int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len) | |
86 | { | |
87 | int ret; | |
88 | u8 txbuf[32] = { 0, }; | |
89 | struct spi_transfer t = { | |
90 | .speed_hz = 2000000, | |
91 | .rx_buf = buf, | |
92 | .len = len, | |
93 | }; | |
94 | struct spi_message m; | |
95 | ||
96 | if (!par->spi) { | |
97 | dev_err(par->info->device, | |
98 | "%s: par->spi is unexpectedly NULL\n", __func__); | |
99 | return -ENODEV; | |
100 | } | |
101 | ||
102 | if (par->startbyte) { | |
103 | if (len > 32) { | |
104 | dev_err(par->info->device, | |
aed1c72e HM |
105 | "len=%zu can't be larger than 32 when using 'startbyte'\n", |
106 | len); | |
c296d5f9 TP |
107 | return -EINVAL; |
108 | } | |
109 | txbuf[0] = par->startbyte | 0x3; | |
110 | t.tx_buf = txbuf; | |
111 | fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, | |
112 | txbuf, len, "%s(len=%d) txbuf => ", __func__, len); | |
113 | } | |
114 | ||
115 | spi_message_init(&m); | |
116 | spi_message_add_tail(&t, &m); | |
117 | ret = spi_sync(par->spi, &m); | |
118 | fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, buf, len, | |
119 | "%s(len=%d) buf <= ", __func__, len); | |
120 | ||
121 | return ret; | |
122 | } | |
123 | EXPORT_SYMBOL(fbtft_read_spi); | |
124 | ||
c296d5f9 TP |
125 | /* |
126 | * Optimized use of gpiolib is twice as fast as no optimization | |
127 | * only one driver can use the optimized version at a time | |
128 | */ | |
129 | int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len) | |
130 | { | |
131 | u8 data; | |
132 | int i; | |
133 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO | |
134 | static u8 prev_data; | |
135 | #endif | |
136 | ||
137 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, | |
138 | "%s(len=%d): ", __func__, len); | |
139 | ||
140 | while (len--) { | |
8d771ea8 | 141 | data = *(u8 *)buf; |
c296d5f9 TP |
142 | |
143 | /* Start writing by pulling down /WR */ | |
144 | gpio_set_value(par->gpio.wr, 0); | |
145 | ||
146 | /* Set data */ | |
147 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO | |
148 | if (data == prev_data) { | |
149 | gpio_set_value(par->gpio.wr, 0); /* used as delay */ | |
150 | } else { | |
151 | for (i = 0; i < 8; i++) { | |
152 | if ((data & 1) != (prev_data & 1)) | |
153 | gpio_set_value(par->gpio.db[i], | |
248d8285 | 154 | data & 1); |
c296d5f9 TP |
155 | data >>= 1; |
156 | prev_data >>= 1; | |
157 | } | |
158 | } | |
159 | #else | |
160 | for (i = 0; i < 8; i++) { | |
248d8285 | 161 | gpio_set_value(par->gpio.db[i], data & 1); |
c296d5f9 TP |
162 | data >>= 1; |
163 | } | |
164 | #endif | |
165 | ||
166 | /* Pullup /WR */ | |
167 | gpio_set_value(par->gpio.wr, 1); | |
168 | ||
169 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO | |
8d771ea8 | 170 | prev_data = *(u8 *)buf; |
c296d5f9 TP |
171 | #endif |
172 | buf++; | |
173 | } | |
174 | ||
175 | return 0; | |
176 | } | |
177 | EXPORT_SYMBOL(fbtft_write_gpio8_wr); | |
178 | ||
179 | int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len) | |
180 | { | |
181 | u16 data; | |
182 | int i; | |
183 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO | |
184 | static u16 prev_data; | |
185 | #endif | |
186 | ||
187 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, | |
188 | "%s(len=%d): ", __func__, len); | |
189 | ||
190 | while (len) { | |
8d771ea8 | 191 | data = *(u16 *)buf; |
c296d5f9 TP |
192 | |
193 | /* Start writing by pulling down /WR */ | |
194 | gpio_set_value(par->gpio.wr, 0); | |
195 | ||
196 | /* Set data */ | |
197 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO | |
198 | if (data == prev_data) { | |
199 | gpio_set_value(par->gpio.wr, 0); /* used as delay */ | |
200 | } else { | |
201 | for (i = 0; i < 16; i++) { | |
202 | if ((data & 1) != (prev_data & 1)) | |
203 | gpio_set_value(par->gpio.db[i], | |
248d8285 | 204 | data & 1); |
c296d5f9 TP |
205 | data >>= 1; |
206 | prev_data >>= 1; | |
207 | } | |
208 | } | |
209 | #else | |
210 | for (i = 0; i < 16; i++) { | |
248d8285 | 211 | gpio_set_value(par->gpio.db[i], data & 1); |
c296d5f9 TP |
212 | data >>= 1; |
213 | } | |
214 | #endif | |
215 | ||
216 | /* Pullup /WR */ | |
217 | gpio_set_value(par->gpio.wr, 1); | |
218 | ||
219 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO | |
8d771ea8 | 220 | prev_data = *(u16 *)buf; |
c296d5f9 TP |
221 | #endif |
222 | buf += 2; | |
223 | len -= 2; | |
224 | } | |
225 | ||
226 | return 0; | |
227 | } | |
228 | EXPORT_SYMBOL(fbtft_write_gpio16_wr); | |
229 | ||
230 | int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len) | |
231 | { | |
232 | dev_err(par->info->device, "%s: function not implemented\n", __func__); | |
233 | return -1; | |
234 | } | |
235 | EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched); |