Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 1 | #include <linux/export.h> |
| 2 | #include <linux/errno.h> |
| 3 | #include <linux/gpio.h> |
| 4 | #include <linux/spi/spi.h> |
| 5 | #include "fbtft.h" |
| 6 | |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 7 | /***************************************************************************** |
| 8 | * |
| 9 | * void (*write_reg)(struct fbtft_par *par, int len, ...); |
| 10 | * |
| 11 | *****************************************************************************/ |
| 12 | |
| 13 | #define define_fbtft_write_reg(func, type, modifier) \ |
| 14 | void func(struct fbtft_par *par, int len, ...) \ |
| 15 | { \ |
| 16 | va_list args; \ |
| 17 | int i, ret; \ |
| 18 | int offset = 0; \ |
| 19 | type *buf = (type *)par->buf; \ |
| 20 | \ |
| 21 | if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { \ |
| 22 | va_start(args, len); \ |
| 23 | for (i = 0; i < len; i++) { \ |
| 24 | buf[i] = (type)va_arg(args, unsigned int); \ |
| 25 | } \ |
| 26 | va_end(args); \ |
| 27 | fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, type, buf, len, "%s: ", __func__); \ |
| 28 | } \ |
| 29 | \ |
| 30 | va_start(args, len); \ |
| 31 | \ |
| 32 | if (par->startbyte) { \ |
| 33 | *(u8 *)par->buf = par->startbyte; \ |
| 34 | buf = (type *)(par->buf + 1); \ |
| 35 | offset = 1; \ |
| 36 | } \ |
| 37 | \ |
| 38 | *buf = modifier((type)va_arg(args, unsigned int)); \ |
| 39 | if (par->gpio.dc != -1) \ |
| 40 | gpio_set_value(par->gpio.dc, 0); \ |
Anish Bhatt | 94c0a54 | 2015-09-03 00:53:37 -0700 | [diff] [blame] | 41 | ret = par->fbtftops.write(par, par->buf, sizeof(type) + offset); \ |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 42 | if (ret < 0) { \ |
| 43 | va_end(args); \ |
| 44 | dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); \ |
| 45 | return; \ |
| 46 | } \ |
| 47 | len--; \ |
| 48 | \ |
| 49 | if (par->startbyte) \ |
| 50 | *(u8 *)par->buf = par->startbyte | 0x2; \ |
| 51 | \ |
| 52 | if (len) { \ |
| 53 | i = len; \ |
| 54 | while (i--) { \ |
| 55 | *buf++ = modifier((type)va_arg(args, unsigned int)); \ |
| 56 | } \ |
| 57 | if (par->gpio.dc != -1) \ |
| 58 | gpio_set_value(par->gpio.dc, 1); \ |
Anish Bhatt | 94c0a54 | 2015-09-03 00:53:37 -0700 | [diff] [blame] | 59 | ret = par->fbtftops.write(par, par->buf, \ |
| 60 | len * (sizeof(type) + offset)); \ |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 61 | if (ret < 0) { \ |
| 62 | va_end(args); \ |
| 63 | dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); \ |
| 64 | return; \ |
| 65 | } \ |
| 66 | } \ |
| 67 | va_end(args); \ |
| 68 | } \ |
| 69 | EXPORT_SYMBOL(func); |
| 70 | |
| 71 | define_fbtft_write_reg(fbtft_write_reg8_bus8, u8, ) |
| 72 | define_fbtft_write_reg(fbtft_write_reg16_bus8, u16, cpu_to_be16) |
| 73 | define_fbtft_write_reg(fbtft_write_reg16_bus16, u16, ) |
| 74 | |
| 75 | void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...) |
| 76 | { |
| 77 | va_list args; |
| 78 | int i, ret; |
| 79 | int pad = 0; |
| 80 | u16 *buf = (u16 *)par->buf; |
| 81 | |
| 82 | if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { |
| 83 | va_start(args, len); |
| 84 | for (i = 0; i < len; i++) |
| 85 | *(((u8 *)buf) + i) = (u8)va_arg(args, unsigned int); |
| 86 | va_end(args); |
| 87 | fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, |
| 88 | par->info->device, u8, buf, len, "%s: ", __func__); |
| 89 | } |
| 90 | if (len <= 0) |
| 91 | return; |
| 92 | |
| 93 | if (par->spi && (par->spi->bits_per_word == 8)) { |
| 94 | /* we're emulating 9-bit, pad start of buffer with no-ops |
| 95 | (assuming here that zero is a no-op) */ |
| 96 | pad = (len % 4) ? 4 - (len % 4) : 0; |
| 97 | for (i = 0; i < pad; i++) |
| 98 | *buf++ = 0x000; |
| 99 | } |
| 100 | |
| 101 | va_start(args, len); |
| 102 | *buf++ = (u8)va_arg(args, unsigned int); |
| 103 | i = len - 1; |
| 104 | while (i--) { |
| 105 | *buf = (u8)va_arg(args, unsigned int); |
| 106 | *buf++ |= 0x100; /* dc=1 */ |
| 107 | } |
| 108 | va_end(args); |
| 109 | ret = par->fbtftops.write(par, par->buf, (len + pad) * sizeof(u16)); |
| 110 | if (ret < 0) { |
| 111 | dev_err(par->info->device, |
Haneen Mohammed | aed1c72 | 2015-03-06 21:59:04 +0300 | [diff] [blame] | 112 | "write() failed and returned %d\n", ret); |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 113 | return; |
| 114 | } |
| 115 | } |
| 116 | EXPORT_SYMBOL(fbtft_write_reg8_bus9); |
| 117 | |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 118 | /***************************************************************************** |
| 119 | * |
| 120 | * int (*write_vmem)(struct fbtft_par *par); |
| 121 | * |
| 122 | *****************************************************************************/ |
| 123 | |
| 124 | /* 16 bit pixel over 8-bit databus */ |
| 125 | int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len) |
| 126 | { |
| 127 | u16 *vmem16; |
Janani Ravichandran | 64f93e1 | 2016-02-25 13:34:28 -0500 | [diff] [blame] | 128 | u16 *txbuf16 = par->txbuf.buf; |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 129 | size_t remain; |
| 130 | size_t to_copy; |
| 131 | size_t tx_array_size; |
| 132 | int i; |
| 133 | int ret = 0; |
| 134 | size_t startbyte_size = 0; |
| 135 | |
| 136 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n", |
| 137 | __func__, offset, len); |
| 138 | |
| 139 | remain = len / 2; |
Lars Svensson | 4b6dc17 | 2015-10-07 09:20:13 +0200 | [diff] [blame] | 140 | vmem16 = (u16 *)(par->info->screen_buffer + offset); |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 141 | |
| 142 | if (par->gpio.dc != -1) |
| 143 | gpio_set_value(par->gpio.dc, 1); |
| 144 | |
| 145 | /* non buffered write */ |
| 146 | if (!par->txbuf.buf) |
| 147 | return par->fbtftops.write(par, vmem16, len); |
| 148 | |
| 149 | /* buffered write */ |
| 150 | tx_array_size = par->txbuf.len / 2; |
| 151 | |
| 152 | if (par->startbyte) { |
Janani Ravichandran | 64f93e1 | 2016-02-25 13:34:28 -0500 | [diff] [blame] | 153 | txbuf16 = par->txbuf.buf + 1; |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 154 | tx_array_size -= 2; |
| 155 | *(u8 *)(par->txbuf.buf) = par->startbyte | 0x2; |
| 156 | startbyte_size = 1; |
| 157 | } |
| 158 | |
| 159 | while (remain) { |
Alison Schofield | 3589d3c | 2016-02-24 09:20:17 -0800 | [diff] [blame] | 160 | to_copy = min(tx_array_size, remain); |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 161 | dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n", |
| 162 | to_copy, remain - to_copy); |
| 163 | |
| 164 | for (i = 0; i < to_copy; i++) |
| 165 | txbuf16[i] = cpu_to_be16(vmem16[i]); |
| 166 | |
| 167 | vmem16 = vmem16 + to_copy; |
| 168 | ret = par->fbtftops.write(par, par->txbuf.buf, |
| 169 | startbyte_size + to_copy * 2); |
| 170 | if (ret < 0) |
| 171 | return ret; |
| 172 | remain -= to_copy; |
| 173 | } |
| 174 | |
| 175 | return ret; |
| 176 | } |
| 177 | EXPORT_SYMBOL(fbtft_write_vmem16_bus8); |
| 178 | |
| 179 | /* 16 bit pixel over 9-bit SPI bus: dc + high byte, dc + low byte */ |
| 180 | int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len) |
| 181 | { |
Lars Svensson | 6b626c7 | 2015-10-07 09:20:14 +0200 | [diff] [blame] | 182 | u8 *vmem8; |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 183 | u16 *txbuf16 = par->txbuf.buf; |
| 184 | size_t remain; |
| 185 | size_t to_copy; |
| 186 | size_t tx_array_size; |
| 187 | int i; |
| 188 | int ret = 0; |
| 189 | |
| 190 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n", |
| 191 | __func__, offset, len); |
| 192 | |
| 193 | if (!par->txbuf.buf) { |
| 194 | dev_err(par->info->device, "%s: txbuf.buf is NULL\n", __func__); |
| 195 | return -1; |
| 196 | } |
| 197 | |
| 198 | remain = len; |
Lars Svensson | 4b6dc17 | 2015-10-07 09:20:13 +0200 | [diff] [blame] | 199 | vmem8 = par->info->screen_buffer + offset; |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 200 | |
| 201 | tx_array_size = par->txbuf.len / 2; |
| 202 | |
| 203 | while (remain) { |
Alison Schofield | 3589d3c | 2016-02-24 09:20:17 -0800 | [diff] [blame] | 204 | to_copy = min(tx_array_size, remain); |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 205 | dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n", |
| 206 | to_copy, remain - to_copy); |
| 207 | |
| 208 | #ifdef __LITTLE_ENDIAN |
| 209 | for (i = 0; i < to_copy; i += 2) { |
Lars Svensson | 6b626c7 | 2015-10-07 09:20:14 +0200 | [diff] [blame] | 210 | txbuf16[i] = 0x0100 | vmem8[i + 1]; |
| 211 | txbuf16[i + 1] = 0x0100 | vmem8[i]; |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 212 | } |
| 213 | #else |
| 214 | for (i = 0; i < to_copy; i++) |
Lars Svensson | 6b626c7 | 2015-10-07 09:20:14 +0200 | [diff] [blame] | 215 | txbuf16[i] = 0x0100 | vmem8[i]; |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 216 | #endif |
| 217 | vmem8 = vmem8 + to_copy; |
Anish Bhatt | 94c0a54 | 2015-09-03 00:53:37 -0700 | [diff] [blame] | 218 | ret = par->fbtftops.write(par, par->txbuf.buf, to_copy * 2); |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 219 | if (ret < 0) |
| 220 | return ret; |
| 221 | remain -= to_copy; |
| 222 | } |
| 223 | |
| 224 | return ret; |
| 225 | } |
| 226 | EXPORT_SYMBOL(fbtft_write_vmem16_bus9); |
| 227 | |
| 228 | int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len) |
| 229 | { |
| 230 | dev_err(par->info->device, "%s: function not implemented\n", __func__); |
| 231 | return -1; |
| 232 | } |
| 233 | EXPORT_SYMBOL(fbtft_write_vmem8_bus8); |
| 234 | |
| 235 | /* 16 bit pixel over 16-bit databus */ |
| 236 | int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len) |
| 237 | { |
| 238 | u16 *vmem16; |
| 239 | |
| 240 | fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n", |
| 241 | __func__, offset, len); |
| 242 | |
Lars Svensson | 4b6dc17 | 2015-10-07 09:20:13 +0200 | [diff] [blame] | 243 | vmem16 = (u16 *)(par->info->screen_buffer + offset); |
Thomas Petazzoni | c296d5f | 2014-12-31 10:11:09 +0100 | [diff] [blame] | 244 | |
| 245 | if (par->gpio.dc != -1) |
| 246 | gpio_set_value(par->gpio.dc, 1); |
| 247 | |
| 248 | /* no need for buffered write with 16-bit bus */ |
| 249 | return par->fbtftops.write(par, vmem16, len); |
| 250 | } |
| 251 | EXPORT_SYMBOL(fbtft_write_vmem16_bus16); |