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