Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / fbtft / fb_agm1264k-fl.c
1 /*
2  * FB driver for Two KS0108 LCD controllers in AGM1264K-FL display
3  *
4  * Copyright (C) 2014 ololoshka2871
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/init.h>
20 #include <linux/gpio.h>
21 #include <linux/delay.h>
22 #include <linux/slab.h>
23
24 #include "fbtft.h"
25
26 /* Uncomment text line to use negative image on display */
27 /*#define NEGATIVE*/
28
29 #define WHITE           0xff
30 #define BLACK           0
31
32 #define DRVNAME         "fb_agm1264k-fl"
33 #define WIDTH           64
34 #define HEIGHT          64
35 #define TOTALWIDTH      (WIDTH * 2)      /* because 2 x ks0108 in one display */
36 #define FPS                     20
37
38 #define EPIN            gpio.wr
39 #define RS                      gpio.dc
40 #define RW                      gpio.aux[2]
41 #define CS0                     gpio.aux[0]
42 #define CS1                     gpio.aux[1]
43
44
45 /* diffusing error (\93Floyd-Steinberg\94) */
46 #define DIFFUSING_MATRIX_WIDTH  2
47 #define DIFFUSING_MATRIX_HEIGHT 2
48
49 static const signed char
50 diffusing_matrix[DIFFUSING_MATRIX_WIDTH][DIFFUSING_MATRIX_HEIGHT] = {
51         {-1, 3},
52         {3, 2},
53 };
54
55 static const unsigned char gamma_correction_table[] = {
56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
57 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6,
58 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13,
59 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21,
60 22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32,
61 33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45,
62 46, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
63 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81,
64 82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102,
65 103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121,
66 123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143,
67 145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166,
68 168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192,
69 194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219,
70 221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248,
71 251, 253, 255
72 };
73
74 static int init_display(struct fbtft_par *par)
75 {
76         u8 i;
77
78         fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
79
80         par->fbtftops.reset(par);
81
82         for (i = 0; i < 2; ++i) {
83                 write_reg(par, i, 0x3f); /* display on */
84                 write_reg(par, i, 0x40); /* set x to 0 */
85                 write_reg(par, i, 0xb0); /* set page to 0 */
86                 write_reg(par, i, 0xc0); /* set start line to 0 */
87         }
88
89         return 0;
90 }
91
92 static void reset(struct fbtft_par *par)
93 {
94         if (par->gpio.reset == -1)
95                 return;
96
97         fbtft_dev_dbg(DEBUG_RESET, par, par->info->device, "%s()\n", __func__);
98
99         gpio_set_value(par->gpio.reset, 0);
100         udelay(20);
101         gpio_set_value(par->gpio.reset, 1);
102         mdelay(120);
103 }
104
105 /* Check if all necessary GPIOS defined */
106 static int verify_gpios(struct fbtft_par *par)
107 {
108         int i;
109
110         fbtft_dev_dbg(DEBUG_VERIFY_GPIOS, par, par->info->device,
111                 "%s()\n", __func__);
112
113         if (par->EPIN < 0) {
114                 dev_err(par->info->device,
115                         "Missing info about 'wr' (aka E) gpio. Aborting.\n");
116                 return -EINVAL;
117         }
118         for (i = 0; i < 8; ++i) {
119                 if (par->gpio.db[i] < 0) {
120                         dev_err(par->info->device,
121                                 "Missing info about 'db[%i]' gpio. Aborting.\n",
122                                 i);
123                         return -EINVAL;
124                 }
125         }
126         if (par->CS0 < 0) {
127                 dev_err(par->info->device,
128                         "Missing info about 'cs0' gpio. Aborting.\n");
129                 return -EINVAL;
130         }
131         if (par->CS1 < 0) {
132                 dev_err(par->info->device,
133                         "Missing info about 'cs1' gpio. Aborting.\n");
134                 return -EINVAL;
135         }
136         if (par->RW < 0) {
137                 dev_err(par->info->device,
138                         "Missing info about 'rw' gpio. Aborting.\n");
139                 return -EINVAL;
140         }
141
142         return 0;
143 }
144
145 static unsigned long
146 request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio)
147 {
148         fbtft_dev_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, par->info->device,
149                 "%s('%s')\n", __func__, gpio->name);
150
151         if (strcasecmp(gpio->name, "wr") == 0) {
152                 /* left ks0108 E pin */
153                 par->EPIN = gpio->gpio;
154                 return GPIOF_OUT_INIT_LOW;
155         } else if (strcasecmp(gpio->name, "cs0") == 0) {
156                 /* left ks0108 controller pin */
157                 par->CS0 = gpio->gpio;
158                 return GPIOF_OUT_INIT_HIGH;
159         } else if (strcasecmp(gpio->name, "cs1") == 0) {
160                 /* right ks0108 controller pin */
161                 par->CS1 = gpio->gpio;
162                 return GPIOF_OUT_INIT_HIGH;
163         }
164
165         /* if write (rw = 0) e(1->0) perform write */
166         /* if read (rw = 1) e(0->1) set data on D0-7*/
167         else if (strcasecmp(gpio->name, "rw") == 0) {
168                 par->RW = gpio->gpio;
169                 return GPIOF_OUT_INIT_LOW;
170         }
171
172         return FBTFT_GPIO_NO_MATCH;
173 }
174
175 /* This function oses to enter commands
176  * first byte - destination controller 0 or 1
177  * following - commands
178  */
179 static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
180 {
181         va_list args;
182         int i, ret;
183         u8 *buf = (u8 *)par->buf;
184
185         if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
186                 va_start(args, len);
187                 for (i = 0; i < len; i++)
188                         buf[i] = (u8)va_arg(args, unsigned int);
189
190                 va_end(args);
191                 fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,
192                         par->info->device, u8, buf, len, "%s: ", __func__);
193         }
194
195         va_start(args, len);
196
197         *buf = (u8)va_arg(args, unsigned int);
198
199         if (*buf > 1) {
200                 va_end(args);
201                 dev_err(par->info->device,
202                         "Incorrect chip select request (%d)\n", *buf);
203                 return;
204         }
205
206         /* select chip */
207         if (*buf) {
208                 /* cs1 */
209                 gpio_set_value(par->CS0, 1);
210                 gpio_set_value(par->CS1, 0);
211         } else {
212                 /* cs0 */
213                 gpio_set_value(par->CS0, 0);
214                 gpio_set_value(par->CS1, 1);
215         }
216
217         gpio_set_value(par->RS, 0); /* RS->0 (command mode) */
218         len--;
219
220         if (len) {
221                 i = len;
222                 while (i--)
223                         *buf++ = (u8)va_arg(args, unsigned int);
224                 ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8)));
225                 if (ret < 0) {
226                         va_end(args);
227                         dev_err(par->info->device,
228                                 "write() failed and returned %d\n", ret);
229                         return;
230                 }
231         }
232
233         va_end(args);
234 }
235
236 static struct
237 {
238         int xs, ys_page, xe, ye_page;
239 } addr_win;
240
241 /* save display writing zone */
242 static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
243 {
244         addr_win.xs = xs;
245         addr_win.ys_page = ys / 8;
246         addr_win.xe = xe;
247         addr_win.ye_page = ye / 8;
248
249         fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
250                 "%s(xs=%d, ys_page=%d, xe=%d, ye_page=%d)\n", __func__,
251                 addr_win.xs, addr_win.ys_page, addr_win.xe, addr_win.ye_page);
252 }
253
254 static void
255 construct_line_bitmap(struct fbtft_par *par, u8 *dest, signed short *src,
256                                                 int xs, int xe, int y)
257 {
258         int x, i;
259
260         for (x = xs; x < xe; ++x) {
261                 u8 res = 0;
262
263                 for (i = 0; i < 8; i++)
264                         if (src[(y * 8 + i) * par->info->var.xres + x])
265                                 res |= 1 << i;
266 #ifdef NEGATIVE
267                 *dest++ = res;
268 #else
269                 *dest++ = ~res;
270 #endif
271         }
272 }
273
274 static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
275 {
276         u16 *vmem16 = (u16 *)par->info->screen_base;
277         u8 *buf = par->txbuf.buf;
278         int x, y;
279         int ret = 0;
280
281         /* buffer to convert RGB565 -> grayscale16 -> Dithered image 1bpp */
282         signed short *convert_buf = kmalloc(par->info->var.xres *
283                 par->info->var.yres * sizeof(signed short), GFP_NOIO);
284
285         if (!convert_buf)
286                 return -ENOMEM;
287
288         fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
289
290         /* converting to grayscale16 */
291         for (x = 0; x < par->info->var.xres; ++x)
292                 for (y = 0; y < par->info->var.yres; ++y) {
293                         u16 pixel = vmem16[y *  par->info->var.xres + x];
294                         u16 b = pixel & 0x1f;
295                         u16 g = (pixel & (0x3f << 5)) >> 5;
296                         u16 r = (pixel & (0x1f << (5 + 6))) >> (5 + 6);
297
298                         pixel = (299 * r + 587 * g + 114 * b) / 200;
299                         if (pixel > 255)
300                                 pixel = 255;
301
302                         /* gamma-correction by table */
303                         convert_buf[y *  par->info->var.xres + x] =
304                                 (signed short)gamma_correction_table[pixel];
305                 }
306
307         /* Image Dithering */
308         for (x = 0; x < par->info->var.xres; ++x)
309                 for (y = 0; y < par->info->var.yres; ++y) {
310                         signed short pixel =
311                                 convert_buf[y *  par->info->var.xres + x];
312                         signed short error_b = pixel - BLACK;
313                         signed short error_w = pixel - WHITE;
314                         signed short error;
315                         u16 i, j;
316
317                         /* what color close? */
318                         if (abs(error_b) >= abs(error_w)) {
319                                 /* white */
320                                 error = error_w;
321                                 pixel = 0xff;
322                         } else {
323                                 /* black */
324                                 error = error_b;
325                                 pixel = 0;
326                         }
327
328                         error /= 8;
329
330                         /* diffusion matrix row */
331                         for (i = 0; i < DIFFUSING_MATRIX_WIDTH; ++i)
332                                 /* diffusion matrix column */
333                                 for (j = 0; j < DIFFUSING_MATRIX_HEIGHT; ++j) {
334                                         signed short *write_pos;
335                                         signed char coeff;
336
337                                         /* skip pixels out of zone */
338                                         if (x + i < 0 ||
339                                                 x + i >= par->info->var.xres
340                                                 || y + j >= par->info->var.yres)
341                                                 continue;
342                                         write_pos = &convert_buf[
343                                                 (y + j) * par->info->var.xres +
344                                                 x + i];
345                                         coeff = diffusing_matrix[i][j];
346                                         if (coeff == -1)
347                                                 /* pixel itself */
348                                                 *write_pos = pixel;
349                                         else {
350                                                 signed short p = *write_pos +
351                                                         error * coeff;
352
353                                                 if (p > WHITE)
354                                                         p = WHITE;
355                                                 if (p < BLACK)
356                                                         p = BLACK;
357                                                 *write_pos = p;
358                                         }
359                                 }
360                 }
361
362          /* 1 string = 2 pages */
363          for (y = addr_win.ys_page; y <= addr_win.ye_page; ++y) {
364                 /* left half of display */
365                 if (addr_win.xs < par->info->var.xres / 2) {
366                         construct_line_bitmap(par, buf, convert_buf,
367                                 addr_win.xs, par->info->var.xres / 2, y);
368
369                         len = par->info->var.xres / 2 - addr_win.xs;
370
371                         /* select left side (sc0)
372                          * set addr
373                          */
374                         write_reg(par, 0x00, (1 << 6) | (u8)addr_win.xs);
375                         write_reg(par, 0x00, (0x17 << 3) | (u8)y);
376
377                         /* write bitmap */
378                         gpio_set_value(par->RS, 1); /* RS->1 (data mode) */
379                         ret = par->fbtftops.write(par, buf, len);
380                         if (ret < 0)
381                                 dev_err(par->info->device,
382                                         "write failed and returned: %d\n",
383                                         ret);
384                 }
385                 /* right half of display */
386                 if (addr_win.xe >= par->info->var.xres / 2) {
387                         construct_line_bitmap(par, buf,
388                                 convert_buf, par->info->var.xres / 2,
389                                 addr_win.xe + 1, y);
390
391                         len = addr_win.xe + 1 - par->info->var.xres / 2;
392
393                         /* select right side (sc1)
394                          * set addr
395                          */
396                         write_reg(par, 0x01, 1 << 6);
397                         write_reg(par, 0x01, (0x17 << 3) | (u8)y);
398
399                         /* write bitmap */
400                         gpio_set_value(par->RS, 1); /* RS->1 (data mode) */
401                         par->fbtftops.write(par, buf, len);
402                         if (ret < 0)
403                                 dev_err(par->info->device,
404                                         "write failed and returned: %d\n",
405                                         ret);
406                 }
407         }
408         kfree(convert_buf);
409
410         gpio_set_value(par->CS0, 1);
411         gpio_set_value(par->CS1, 1);
412
413         return ret;
414 }
415
416 static int write(struct fbtft_par *par, void *buf, size_t len)
417 {
418         fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
419                 "%s(len=%d): ", __func__, len);
420
421         gpio_set_value(par->RW, 0); /* set write mode */
422
423
424         while (len--) {
425                 u8 i, data;
426
427                 data = *(u8 *) buf++;
428
429                 /* set data bus */
430                 for (i = 0; i < 8; ++i)
431                         gpio_set_value(par->gpio.db[i], data & (1 << i));
432                 /* set E */
433                 gpio_set_value(par->EPIN, 1);
434                 udelay(5);
435                 /* unset E - write */
436                 gpio_set_value(par->EPIN, 0);
437                 udelay(1);
438         }
439
440         return 0;
441 }
442
443 static struct fbtft_display display = {
444         .regwidth = 8,
445         .width = TOTALWIDTH,
446         .height = HEIGHT,
447         .fps = FPS,
448         .fbtftops = {
449                 .init_display = init_display,
450                 .set_addr_win = set_addr_win,
451                 .verify_gpios = verify_gpios,
452                 .request_gpios_match = request_gpios_match,
453                 .reset = reset,
454                 .write = write,
455                 .write_register = write_reg8_bus8,
456                 .write_vmem = write_vmem,
457         },
458 };
459 FBTFT_REGISTER_DRIVER(DRVNAME, "displaytronic,fb_agm1264k-fl", &display);
460
461 MODULE_ALIAS("platform:" DRVNAME);
462
463 MODULE_DESCRIPTION("Two KS0108 LCD controllers in AGM1264K-FL display");
464 MODULE_AUTHOR("ololoshka2871");
465 MODULE_LICENSE("GPL");