1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/export.h> |
3 | #include <linux/errno.h> |
4 | #include <linux/gpio/consumer.h> |
5 | #include <linux/spi/spi.h> |
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=%zu): " , __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: &m); |
26 | spi_message_add_tail(t: &t, m: &m); |
27 | return spi_sync(spi: par->spi, message: &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=%zu): " , __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, |
59 | "error: len=%zu must be divisible by 8\n" , len); |
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); |
75 | *(__be64 *)dst = cpu_to_be64(tmp); |
76 | dst += 8; |
77 | *dst++ = (u8)(*src++ & 0x00FF); |
78 | added++; |
79 | } |
80 | |
81 | return spi_write(spi: par->spi, buf: par->extra, len: 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, |
105 | "len=%zu can't be larger than 32 when using 'startbyte'\n" , |
106 | len); |
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=%zu) txbuf => " , |
113 | __func__, len); |
114 | } |
115 | |
116 | spi_message_init(m: &m); |
117 | spi_message_add_tail(t: &t, m: &m); |
118 | ret = spi_sync(spi: par->spi, message: &m); |
119 | fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, buf, len, |
120 | "%s(len=%zu) buf <= " , __func__, len); |
121 | |
122 | return ret; |
123 | } |
124 | EXPORT_SYMBOL(fbtft_read_spi); |
125 | |
126 | /* |
127 | * Optimized use of gpiolib is twice as fast as no optimization |
128 | * only one driver can use the optimized version at a time |
129 | */ |
130 | int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len) |
131 | { |
132 | u8 data; |
133 | int i; |
134 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
135 | static u8 prev_data; |
136 | #endif |
137 | |
138 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, |
139 | "%s(len=%zu): " , __func__, len); |
140 | |
141 | while (len--) { |
142 | data = *(u8 *)buf; |
143 | |
144 | /* Start writing by pulling down /WR */ |
145 | gpiod_set_value(desc: par->gpio.wr, value: 1); |
146 | |
147 | /* Set data */ |
148 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
149 | if (data == prev_data) { |
150 | gpiod_set_value(desc: par->gpio.wr, value: 1); /* used as delay */ |
151 | } else { |
152 | for (i = 0; i < 8; i++) { |
153 | if ((data & 1) != (prev_data & 1)) |
154 | gpiod_set_value(desc: par->gpio.db[i], |
155 | value: data & 1); |
156 | data >>= 1; |
157 | prev_data >>= 1; |
158 | } |
159 | } |
160 | #else |
161 | for (i = 0; i < 8; i++) { |
162 | gpiod_set_value(par->gpio.db[i], data & 1); |
163 | data >>= 1; |
164 | } |
165 | #endif |
166 | |
167 | /* Pullup /WR */ |
168 | gpiod_set_value(desc: par->gpio.wr, value: 0); |
169 | |
170 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
171 | prev_data = *(u8 *)buf; |
172 | #endif |
173 | buf++; |
174 | } |
175 | |
176 | return 0; |
177 | } |
178 | EXPORT_SYMBOL(fbtft_write_gpio8_wr); |
179 | |
180 | int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len) |
181 | { |
182 | u16 data; |
183 | int i; |
184 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
185 | static u16 prev_data; |
186 | #endif |
187 | |
188 | fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, |
189 | "%s(len=%zu): " , __func__, len); |
190 | |
191 | while (len) { |
192 | data = *(u16 *)buf; |
193 | |
194 | /* Start writing by pulling down /WR */ |
195 | gpiod_set_value(desc: par->gpio.wr, value: 1); |
196 | |
197 | /* Set data */ |
198 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
199 | if (data == prev_data) { |
200 | gpiod_set_value(desc: par->gpio.wr, value: 1); /* used as delay */ |
201 | } else { |
202 | for (i = 0; i < 16; i++) { |
203 | if ((data & 1) != (prev_data & 1)) |
204 | gpiod_set_value(desc: par->gpio.db[i], |
205 | value: data & 1); |
206 | data >>= 1; |
207 | prev_data >>= 1; |
208 | } |
209 | } |
210 | #else |
211 | for (i = 0; i < 16; i++) { |
212 | gpiod_set_value(par->gpio.db[i], data & 1); |
213 | data >>= 1; |
214 | } |
215 | #endif |
216 | |
217 | /* Pullup /WR */ |
218 | gpiod_set_value(desc: par->gpio.wr, value: 0); |
219 | |
220 | #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
221 | prev_data = *(u16 *)buf; |
222 | #endif |
223 | buf += 2; |
224 | len -= 2; |
225 | } |
226 | |
227 | return 0; |
228 | } |
229 | EXPORT_SYMBOL(fbtft_write_gpio16_wr); |
230 | |
231 | int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len) |
232 | { |
233 | dev_err(par->info->device, "%s: function not implemented\n" , __func__); |
234 | return -1; |
235 | } |
236 | EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched); |
237 | |