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