GNU Linux-libre 4.14.254-gnu1
[releases.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.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=%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);
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,
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=%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
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--) {
141                 data = *(u8 *)buf;
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],
154                                                                 data & 1);
155                                 data >>= 1;
156                                 prev_data >>= 1;
157                         }
158                 }
159 #else
160                 for (i = 0; i < 8; i++) {
161                         gpio_set_value(par->gpio.db[i], data & 1);
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
170                 prev_data = *(u8 *)buf;
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) {
191                 data = *(u16 *)buf;
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],
204                                                                 data & 1);
205                                 data >>= 1;
206                                 prev_data >>= 1;
207                         }
208                 }
209 #else
210                 for (i = 0; i < 16; i++) {
211                         gpio_set_value(par->gpio.db[i], data & 1);
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
220                 prev_data = *(u16 *)buf;
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);