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