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_uc1611.c (8455B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * FB driver for the UltraChip UC1611 LCD controller
      4 *
      5 * The display is 4-bit grayscale (16 shades) 240x160.
      6 *
      7 * Copyright (C) 2015 Henri Chain
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/kernel.h>
     12#include <linux/init.h>
     13#include <linux/gpio/consumer.h>
     14#include <linux/spi/spi.h>
     15#include <linux/delay.h>
     16
     17#include "fbtft.h"
     18
     19#define DRVNAME		"fb_uc1611"
     20#define WIDTH		240
     21#define HEIGHT		160
     22#define BPP		8
     23#define FPS		40
     24
     25/*
     26 * LCD voltage is a combination of ratio, gain, pot and temp
     27 *
     28 * V_LCD = V_BIAS * ratio
     29 * V_LCD = (C_V0 + C_PM × pot) * (1 + (T - 25) * temp)
     30 * C_V0 and C_PM depend on ratio and gain
     31 * T is ambient temperature
     32 */
     33
     34/* BR -> actual ratio: 0-3 -> 5, 10, 11, 13 */
     35static unsigned int ratio = 2;
     36module_param(ratio, uint, 0000);
     37MODULE_PARM_DESC(ratio, "BR[1:0] Bias voltage ratio: 0-3 (default: 2)");
     38
     39static unsigned int gain = 3;
     40module_param(gain, uint, 0000);
     41MODULE_PARM_DESC(gain, "GN[1:0] Bias voltage gain: 0-3 (default: 3)");
     42
     43static unsigned int pot = 16;
     44module_param(pot, uint, 0000);
     45MODULE_PARM_DESC(pot, "PM[6:0] Bias voltage pot.: 0-63 (default: 16)");
     46
     47/* TC -> % compensation per deg C: 0-3 -> -.05, -.10, -.015, -.20 */
     48static unsigned int temp;
     49module_param(temp, uint, 0000);
     50MODULE_PARM_DESC(temp, "TC[1:0] Temperature compensation: 0-3 (default: 0)");
     51
     52/* PC[1:0] -> LCD capacitance: 0-3 -> <20nF, 20-28 nF, 29-40 nF, 40-56 nF */
     53static unsigned int load = 1;
     54module_param(load, uint, 0000);
     55MODULE_PARM_DESC(load, "PC[1:0] Panel Loading: 0-3 (default: 1)");
     56
     57/* PC[3:2] -> V_LCD: 0, 1, 3 -> ext., int. with ratio = 5, int. standard */
     58static unsigned int pump = 3;
     59module_param(pump, uint, 0000);
     60MODULE_PARM_DESC(pump, "PC[3:2] Pump control: 0,1,3 (default: 3)");
     61
     62static int init_display(struct fbtft_par *par)
     63{
     64	int ret;
     65
     66	/*
     67	 * Set CS active inverse polarity: just setting SPI_CS_HIGH does not
     68	 * work with GPIO based chip selects that are logically active high
     69	 * but inverted inside the GPIO library, so enforce inverted
     70	 * semantics.
     71	 */
     72	par->spi->mode ^= SPI_CS_HIGH;
     73	ret = spi_setup(par->spi);
     74	if (ret) {
     75		dev_err(par->info->device,
     76			"Could not set inverse CS polarity\n");
     77		return ret;
     78	}
     79
     80	/* Reset controller */
     81	write_reg(par, 0xE2);
     82
     83	/* Set bias ratio */
     84	write_reg(par, 0xE8 | (ratio & 0x03));
     85
     86	/* Set bias gain and potentiometer */
     87	write_reg(par, 0x81);
     88	write_reg(par, (gain & 0x03) << 6 | (pot & 0x3F));
     89
     90	/* Set temperature compensation */
     91	write_reg(par, 0x24 | (temp & 0x03));
     92
     93	/* Set panel loading */
     94	write_reg(par, 0x28 | (load & 0x03));
     95
     96	/* Set pump control */
     97	write_reg(par, 0x2C | (pump & 0x03));
     98
     99	/* Set inverse display */
    100	write_reg(par, 0xA6 | 0x01);
    101
    102	/* Set 4-bit grayscale mode */
    103	write_reg(par, 0xD0 | (0x02 & 0x03));
    104
    105	/* Set Display enable */
    106	write_reg(par, 0xA8 | 0x07);
    107
    108	return 0;
    109}
    110
    111static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
    112{
    113	switch (par->info->var.rotate) {
    114	case 90:
    115	case 270:
    116		/* Set column address */
    117		write_reg(par, ys & 0x0F);
    118		write_reg(par, 0x10 | (ys >> 4));
    119
    120		/* Set page address (divide xs by 2) (not used by driver) */
    121		write_reg(par, 0x60 | ((xs >> 1) & 0x0F));
    122		write_reg(par, 0x70 | (xs >> 5));
    123		break;
    124	default:
    125		/* Set column address (not used by driver) */
    126		write_reg(par, xs & 0x0F);
    127		write_reg(par, 0x10 | (xs >> 4));
    128
    129		/* Set page address (divide ys by 2) */
    130		write_reg(par, 0x60 | ((ys >> 1) & 0x0F));
    131		write_reg(par, 0x70 | (ys >> 5));
    132		break;
    133	}
    134}
    135
    136static int blank(struct fbtft_par *par, bool on)
    137{
    138	fbtft_par_dbg(DEBUG_BLANK, par, "(%s=%s)\n",
    139		      __func__, on ? "true" : "false");
    140
    141	if (on)
    142		write_reg(par, 0xA8 | 0x00);
    143	else
    144		write_reg(par, 0xA8 | 0x07);
    145	return 0;
    146}
    147
    148static int set_var(struct fbtft_par *par)
    149{
    150	/* par->info->fix.visual = FB_VISUAL_PSEUDOCOLOR; */
    151	par->info->var.grayscale = 1;
    152	par->info->var.red.offset    = 0;
    153	par->info->var.red.length    = 8;
    154	par->info->var.green.offset  = 0;
    155	par->info->var.green.length  = 8;
    156	par->info->var.blue.offset   = 0;
    157	par->info->var.blue.length   = 8;
    158	par->info->var.transp.offset = 0;
    159	par->info->var.transp.length = 0;
    160
    161	switch (par->info->var.rotate) {
    162	case 90:
    163		/* Set RAM address control */
    164		write_reg(par, 0x88
    165			| (0x0 & 0x1) << 2 /* Increment positively */
    166			| (0x1 << 1)	   /* Increment page first */
    167			| 0x1);		   /* Wrap around (default) */
    168
    169		/* Set LCD mapping */
    170		write_reg(par, 0xC0
    171			| (0x0 & 0x1) << 2 /* Mirror Y OFF */
    172			| (0x0 & 0x1) << 1 /* Mirror X OFF */
    173			| (0x0 & 0x1));    /* MS nibble last (default) */
    174		break;
    175	case 180:
    176		/* Set RAM address control */
    177		write_reg(par, 0x88
    178			| (0x0 & 0x1) << 2 /* Increment positively */
    179			| (0x0 & 0x1) << 1 /* Increment column first */
    180			| 0x1);		   /* Wrap around (default) */
    181
    182		/* Set LCD mapping */
    183		write_reg(par, 0xC0
    184			| (0x1 << 2)	   /* Mirror Y ON */
    185			| (0x0 & 0x1) << 1 /* Mirror X OFF */
    186			| (0x0 & 0x1));    /* MS nibble last (default) */
    187		break;
    188	case 270:
    189		/* Set RAM address control */
    190		write_reg(par, 0x88
    191			| (0x0 & 0x1) << 2 /* Increment positively */
    192			| (0x1 << 1)	   /* Increment page first */
    193			| 0x1);		   /* Wrap around (default) */
    194
    195		/* Set LCD mapping */
    196		write_reg(par, 0xC0
    197			| (0x1 << 2)	   /* Mirror Y ON */
    198			| (0x1 << 1)	   /* Mirror X ON */
    199			| (0x0 & 0x1));    /* MS nibble last (default) */
    200		break;
    201	default:
    202		/* Set RAM address control */
    203		write_reg(par, 0x88
    204			| (0x0 & 0x1) << 2 /* Increment positively */
    205			| (0x0 & 0x1) << 1 /* Increment column first */
    206			| 0x1);		   /* Wrap around (default) */
    207
    208		/* Set LCD mapping */
    209		write_reg(par, 0xC0
    210			| (0x0 & 0x1) << 2 /* Mirror Y OFF */
    211			| (0x1 << 1)	   /* Mirror X ON */
    212			| (0x0 & 0x1));    /* MS nibble last (default) */
    213		break;
    214	}
    215
    216	return 0;
    217}
    218
    219static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
    220{
    221	u8 *vmem8 = (u8 *)(par->info->screen_buffer);
    222	u8 *buf8 = par->txbuf.buf;
    223	u16 *buf16 = par->txbuf.buf;
    224	int line_length = par->info->fix.line_length;
    225	int y_start = offset / line_length;
    226	int y_end = (offset + len - 1) / line_length;
    227	int x, y, i;
    228	int ret = 0;
    229
    230	switch (par->pdata->display.buswidth) {
    231	case 8:
    232		switch (par->info->var.rotate) {
    233		case 90:
    234		case 270:
    235			i = y_start * line_length;
    236			for (y = y_start; y <= y_end; y++) {
    237				for (x = 0; x < line_length; x += 2) {
    238					*buf8 = vmem8[i] >> 4;
    239					*buf8 |= vmem8[i + 1] & 0xF0;
    240					buf8++;
    241					i += 2;
    242				}
    243			}
    244			break;
    245		default:
    246			/* Must be even because pages are two lines */
    247			y_start &= 0xFE;
    248			i = y_start * line_length;
    249			for (y = y_start; y <= y_end; y += 2) {
    250				for (x = 0; x < line_length; x++) {
    251					*buf8 = vmem8[i] >> 4;
    252					*buf8 |= vmem8[i + line_length] & 0xF0;
    253					buf8++;
    254					i++;
    255				}
    256				i += line_length;
    257			}
    258			break;
    259		}
    260		gpiod_set_value(par->gpio.dc, 1);
    261
    262		/* Write data */
    263		ret = par->fbtftops.write(par, par->txbuf.buf, len / 2);
    264		break;
    265	case 9:
    266		switch (par->info->var.rotate) {
    267		case 90:
    268		case 270:
    269			i = y_start * line_length;
    270			for (y = y_start; y <= y_end; y++) {
    271				for (x = 0; x < line_length; x += 2) {
    272					*buf16 = 0x100;
    273					*buf16 |= vmem8[i] >> 4;
    274					*buf16 |= vmem8[i + 1] & 0xF0;
    275					buf16++;
    276					i += 2;
    277				}
    278			}
    279			break;
    280		default:
    281			/* Must be even because pages are two lines */
    282			y_start &= 0xFE;
    283			i = y_start * line_length;
    284			for (y = y_start; y <= y_end; y += 2) {
    285				for (x = 0; x < line_length; x++) {
    286					*buf16 = 0x100;
    287					*buf16 |= vmem8[i] >> 4;
    288					*buf16 |= vmem8[i + line_length] & 0xF0;
    289					buf16++;
    290					i++;
    291				}
    292				i += line_length;
    293			}
    294			break;
    295		}
    296
    297		/* Write data */
    298		ret = par->fbtftops.write(par, par->txbuf.buf, len);
    299		break;
    300	default:
    301		dev_err(par->info->device, "unsupported buswidth %d\n",
    302			par->pdata->display.buswidth);
    303	}
    304
    305	if (ret < 0)
    306		dev_err(par->info->device, "write failed and returned: %d\n",
    307			ret);
    308
    309	return ret;
    310}
    311
    312static struct fbtft_display display = {
    313	.txbuflen = -1,
    314	.regwidth = 8,
    315	.width = WIDTH,
    316	.height = HEIGHT,
    317	.bpp = BPP,
    318	.fps = FPS,
    319	.fbtftops = {
    320		.write_vmem = write_vmem,
    321		.init_display = init_display,
    322		.set_addr_win = set_addr_win,
    323		.set_var = set_var,
    324		.blank = blank,
    325	},
    326};
    327
    328FBTFT_REGISTER_DRIVER(DRVNAME, "ultrachip,uc1611", &display);
    329
    330MODULE_ALIAS("spi:" DRVNAME);
    331MODULE_ALIAS("platform:" DRVNAME);
    332MODULE_ALIAS("spi:uc1611");
    333MODULE_ALIAS("platform:uc1611");
    334
    335MODULE_DESCRIPTION("FB driver for the UC1611 LCD controller");
    336MODULE_AUTHOR("Henri Chain");
    337MODULE_LICENSE("GPL");