Linux-libre 5.4.47-gnu
[librecmc/linux-libre.git] / drivers / staging / fbtft / fbtft-io.c
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);
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=%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(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,
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);
117         spi_message_add_tail(&t, &m);
118         ret = spi_sync(par->spi, &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(par->gpio.wr, 0);
146
147                 /* Set data */
148 #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
149                 if (data == prev_data) {
150                         gpiod_set_value(par->gpio.wr, 0); /* used as delay */
151                 } else {
152                         for (i = 0; i < 8; i++) {
153                                 if ((data & 1) != (prev_data & 1))
154                                         gpiod_set_value(par->gpio.db[i],
155                                                         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(par->gpio.wr, 1);
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(par->gpio.wr, 0);
196
197                 /* Set data */
198 #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
199                 if (data == prev_data) {
200                         gpiod_set_value(par->gpio.wr, 0); /* used as delay */
201                 } else {
202                         for (i = 0; i < 16; i++) {
203                                 if ((data & 1) != (prev_data & 1))
204                                         gpiod_set_value(par->gpio.db[i],
205                                                         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(par->gpio.wr, 1);
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);