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 (�Floyd-Steinberg�) */ 46#define DIFFUSING_MATRIX_WIDTH 2 47#define DIFFUSING_MATRIX_HEIGHT 2 48 49static const signed char 50diffusing_matrix[DIFFUSING_MATRIX_WIDTH][DIFFUSING_MATRIX_HEIGHT] = { 51 {-1, 3}, 52 {3, 2}, 53}; 54 55static const unsigned char gamma_correction_table[] = { 560, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 571, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 586, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 5913, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 6022, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 6133, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 6246, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 6362, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81, 6482, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102, 65103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121, 66123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143, 67145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166, 68168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192, 69194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 70221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248, 71251, 253, 255 72}; 73 74static 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 92static 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 */ 106static 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 145static unsigned long 146request_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 */ 179static 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 236static struct 237{ 238 int xs, ys_page, xe, ye_page; 239} addr_win; 240 241/* save display writing zone */ 242static 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 254static void 255construct_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 274static 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 416static 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 443static 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}; 459FBTFT_REGISTER_DRIVER(DRVNAME, "displaytronic,fb_agm1264k-fl", &display); 460 461MODULE_ALIAS("platform:" DRVNAME); 462 463MODULE_DESCRIPTION("Two KS0108 LCD controllers in AGM1264K-FL display"); 464MODULE_AUTHOR("ololoshka2871"); 465MODULE_LICENSE("GPL"); 466