cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fb_agm1264k-fl.c (10729B)


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