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

ams369fg06.c (11741B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * ams369fg06 AMOLED LCD panel driver.
      4 *
      5 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
      6 * Author: Jingoo Han  <jg1.han@samsung.com>
      7 *
      8 * Derived from drivers/video/s6e63m0.c
      9 */
     10
     11#include <linux/backlight.h>
     12#include <linux/delay.h>
     13#include <linux/fb.h>
     14#include <linux/lcd.h>
     15#include <linux/module.h>
     16#include <linux/spi/spi.h>
     17#include <linux/wait.h>
     18
     19#define SLEEPMSEC		0x1000
     20#define ENDDEF			0x2000
     21#define	DEFMASK			0xFF00
     22#define COMMAND_ONLY		0xFE
     23#define DATA_ONLY		0xFF
     24
     25#define MAX_GAMMA_LEVEL		5
     26#define GAMMA_TABLE_COUNT	21
     27
     28#define MIN_BRIGHTNESS		0
     29#define MAX_BRIGHTNESS		255
     30#define DEFAULT_BRIGHTNESS	150
     31
     32struct ams369fg06 {
     33	struct device			*dev;
     34	struct spi_device		*spi;
     35	unsigned int			power;
     36	struct lcd_device		*ld;
     37	struct backlight_device		*bd;
     38	struct lcd_platform_data	*lcd_pd;
     39};
     40
     41static const unsigned short seq_display_on[] = {
     42	0x14, 0x03,
     43	ENDDEF, 0x0000
     44};
     45
     46static const unsigned short seq_display_off[] = {
     47	0x14, 0x00,
     48	ENDDEF, 0x0000
     49};
     50
     51static const unsigned short seq_stand_by_on[] = {
     52	0x1D, 0xA1,
     53	SLEEPMSEC, 200,
     54	ENDDEF, 0x0000
     55};
     56
     57static const unsigned short seq_stand_by_off[] = {
     58	0x1D, 0xA0,
     59	SLEEPMSEC, 250,
     60	ENDDEF, 0x0000
     61};
     62
     63static const unsigned short seq_setting[] = {
     64	0x31, 0x08,
     65	0x32, 0x14,
     66	0x30, 0x02,
     67	0x27, 0x01,
     68	0x12, 0x08,
     69	0x13, 0x08,
     70	0x15, 0x00,
     71	0x16, 0x00,
     72
     73	0xef, 0xd0,
     74	DATA_ONLY, 0xe8,
     75
     76	0x39, 0x44,
     77	0x40, 0x00,
     78	0x41, 0x3f,
     79	0x42, 0x2a,
     80	0x43, 0x27,
     81	0x44, 0x27,
     82	0x45, 0x1f,
     83	0x46, 0x44,
     84	0x50, 0x00,
     85	0x51, 0x00,
     86	0x52, 0x17,
     87	0x53, 0x24,
     88	0x54, 0x26,
     89	0x55, 0x1f,
     90	0x56, 0x43,
     91	0x60, 0x00,
     92	0x61, 0x3f,
     93	0x62, 0x2a,
     94	0x63, 0x25,
     95	0x64, 0x24,
     96	0x65, 0x1b,
     97	0x66, 0x5c,
     98
     99	0x17, 0x22,
    100	0x18, 0x33,
    101	0x19, 0x03,
    102	0x1a, 0x01,
    103	0x22, 0xa4,
    104	0x23, 0x00,
    105	0x26, 0xa0,
    106
    107	0x1d, 0xa0,
    108	SLEEPMSEC, 300,
    109
    110	0x14, 0x03,
    111
    112	ENDDEF, 0x0000
    113};
    114
    115/* gamma value: 2.2 */
    116static const unsigned int ams369fg06_22_250[] = {
    117	0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44,
    118	0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43,
    119	0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c,
    120};
    121
    122static const unsigned int ams369fg06_22_200[] = {
    123	0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e,
    124	0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d,
    125	0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53,
    126};
    127
    128static const unsigned int ams369fg06_22_150[] = {
    129	0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37,
    130	0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36,
    131	0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a,
    132};
    133
    134static const unsigned int ams369fg06_22_100[] = {
    135	0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f,
    136	0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e,
    137	0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f,
    138};
    139
    140static const unsigned int ams369fg06_22_50[] = {
    141	0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24,
    142	0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23,
    143	0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31,
    144};
    145
    146struct ams369fg06_gamma {
    147	unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
    148};
    149
    150static struct ams369fg06_gamma gamma_table = {
    151	.gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50,
    152	.gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100,
    153	.gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150,
    154	.gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200,
    155	.gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250,
    156};
    157
    158static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data)
    159{
    160	u16 buf[1];
    161	struct spi_message msg;
    162
    163	struct spi_transfer xfer = {
    164		.len		= 2,
    165		.tx_buf		= buf,
    166	};
    167
    168	buf[0] = (addr << 8) | data;
    169
    170	spi_message_init(&msg);
    171	spi_message_add_tail(&xfer, &msg);
    172
    173	return spi_sync(lcd->spi, &msg);
    174}
    175
    176static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address,
    177	unsigned char command)
    178{
    179	int ret = 0;
    180
    181	if (address != DATA_ONLY)
    182		ret = ams369fg06_spi_write_byte(lcd, 0x70, address);
    183	if (command != COMMAND_ONLY)
    184		ret = ams369fg06_spi_write_byte(lcd, 0x72, command);
    185
    186	return ret;
    187}
    188
    189static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
    190	const unsigned short *wbuf)
    191{
    192	int ret = 0, i = 0;
    193
    194	while ((wbuf[i] & DEFMASK) != ENDDEF) {
    195		if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
    196			ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
    197			if (ret)
    198				break;
    199		} else {
    200			msleep(wbuf[i+1]);
    201		}
    202		i += 2;
    203	}
    204
    205	return ret;
    206}
    207
    208static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
    209	const unsigned int *gamma)
    210{
    211	unsigned int i = 0;
    212	int ret = 0;
    213
    214	for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) {
    215		ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]);
    216		ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]);
    217		ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]);
    218		if (ret) {
    219			dev_err(lcd->dev, "failed to set gamma table.\n");
    220			goto gamma_err;
    221		}
    222	}
    223
    224gamma_err:
    225	return ret;
    226}
    227
    228static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
    229{
    230	int ret = 0;
    231	int gamma = 0;
    232
    233	if ((brightness >= 0) && (brightness <= 50))
    234		gamma = 0;
    235	else if ((brightness > 50) && (brightness <= 100))
    236		gamma = 1;
    237	else if ((brightness > 100) && (brightness <= 150))
    238		gamma = 2;
    239	else if ((brightness > 150) && (brightness <= 200))
    240		gamma = 3;
    241	else if ((brightness > 200) && (brightness <= 255))
    242		gamma = 4;
    243
    244	ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
    245
    246	return ret;
    247}
    248
    249static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
    250{
    251	int ret, i;
    252	static const unsigned short *init_seq[] = {
    253		seq_setting,
    254		seq_stand_by_off,
    255	};
    256
    257	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
    258		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
    259		if (ret)
    260			break;
    261	}
    262
    263	return ret;
    264}
    265
    266static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
    267{
    268	int ret, i;
    269	static const unsigned short *init_seq[] = {
    270		seq_stand_by_off,
    271		seq_display_on,
    272	};
    273
    274	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
    275		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
    276		if (ret)
    277			break;
    278	}
    279
    280	return ret;
    281}
    282
    283static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
    284{
    285	int ret, i;
    286
    287	static const unsigned short *init_seq[] = {
    288		seq_display_off,
    289		seq_stand_by_on,
    290	};
    291
    292	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
    293		ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
    294		if (ret)
    295			break;
    296	}
    297
    298	return ret;
    299}
    300
    301static int ams369fg06_power_is_on(int power)
    302{
    303	return power <= FB_BLANK_NORMAL;
    304}
    305
    306static int ams369fg06_power_on(struct ams369fg06 *lcd)
    307{
    308	int ret = 0;
    309	struct lcd_platform_data *pd;
    310	struct backlight_device *bd;
    311
    312	pd = lcd->lcd_pd;
    313	bd = lcd->bd;
    314
    315	if (pd->power_on) {
    316		pd->power_on(lcd->ld, 1);
    317		msleep(pd->power_on_delay);
    318	}
    319
    320	if (!pd->reset) {
    321		dev_err(lcd->dev, "reset is NULL.\n");
    322		return -EINVAL;
    323	}
    324
    325	pd->reset(lcd->ld);
    326	msleep(pd->reset_delay);
    327
    328	ret = ams369fg06_ldi_init(lcd);
    329	if (ret) {
    330		dev_err(lcd->dev, "failed to initialize ldi.\n");
    331		return ret;
    332	}
    333
    334	ret = ams369fg06_ldi_enable(lcd);
    335	if (ret) {
    336		dev_err(lcd->dev, "failed to enable ldi.\n");
    337		return ret;
    338	}
    339
    340	/* set brightness to current value after power on or resume. */
    341	ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
    342	if (ret) {
    343		dev_err(lcd->dev, "lcd gamma setting failed.\n");
    344		return ret;
    345	}
    346
    347	return 0;
    348}
    349
    350static int ams369fg06_power_off(struct ams369fg06 *lcd)
    351{
    352	int ret;
    353	struct lcd_platform_data *pd;
    354
    355	pd = lcd->lcd_pd;
    356
    357	ret = ams369fg06_ldi_disable(lcd);
    358	if (ret) {
    359		dev_err(lcd->dev, "lcd setting failed.\n");
    360		return -EIO;
    361	}
    362
    363	msleep(pd->power_off_delay);
    364
    365	if (pd->power_on)
    366		pd->power_on(lcd->ld, 0);
    367
    368	return 0;
    369}
    370
    371static int ams369fg06_power(struct ams369fg06 *lcd, int power)
    372{
    373	int ret = 0;
    374
    375	if (ams369fg06_power_is_on(power) &&
    376		!ams369fg06_power_is_on(lcd->power))
    377		ret = ams369fg06_power_on(lcd);
    378	else if (!ams369fg06_power_is_on(power) &&
    379		ams369fg06_power_is_on(lcd->power))
    380		ret = ams369fg06_power_off(lcd);
    381
    382	if (!ret)
    383		lcd->power = power;
    384
    385	return ret;
    386}
    387
    388static int ams369fg06_get_power(struct lcd_device *ld)
    389{
    390	struct ams369fg06 *lcd = lcd_get_data(ld);
    391
    392	return lcd->power;
    393}
    394
    395static int ams369fg06_set_power(struct lcd_device *ld, int power)
    396{
    397	struct ams369fg06 *lcd = lcd_get_data(ld);
    398
    399	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
    400		power != FB_BLANK_NORMAL) {
    401		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
    402		return -EINVAL;
    403	}
    404
    405	return ams369fg06_power(lcd, power);
    406}
    407
    408static int ams369fg06_set_brightness(struct backlight_device *bd)
    409{
    410	int ret = 0;
    411	int brightness = bd->props.brightness;
    412	struct ams369fg06 *lcd = bl_get_data(bd);
    413
    414	if (brightness < MIN_BRIGHTNESS ||
    415		brightness > bd->props.max_brightness) {
    416		dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
    417			MIN_BRIGHTNESS, MAX_BRIGHTNESS);
    418		return -EINVAL;
    419	}
    420
    421	ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
    422	if (ret) {
    423		dev_err(&bd->dev, "lcd brightness setting failed.\n");
    424		return -EIO;
    425	}
    426
    427	return ret;
    428}
    429
    430static struct lcd_ops ams369fg06_lcd_ops = {
    431	.get_power = ams369fg06_get_power,
    432	.set_power = ams369fg06_set_power,
    433};
    434
    435static const struct backlight_ops ams369fg06_backlight_ops = {
    436	.update_status = ams369fg06_set_brightness,
    437};
    438
    439static int ams369fg06_probe(struct spi_device *spi)
    440{
    441	int ret = 0;
    442	struct ams369fg06 *lcd = NULL;
    443	struct lcd_device *ld = NULL;
    444	struct backlight_device *bd = NULL;
    445	struct backlight_properties props;
    446
    447	lcd = devm_kzalloc(&spi->dev, sizeof(struct ams369fg06), GFP_KERNEL);
    448	if (!lcd)
    449		return -ENOMEM;
    450
    451	/* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
    452	spi->bits_per_word = 16;
    453
    454	ret = spi_setup(spi);
    455	if (ret < 0) {
    456		dev_err(&spi->dev, "spi setup failed.\n");
    457		return ret;
    458	}
    459
    460	lcd->spi = spi;
    461	lcd->dev = &spi->dev;
    462
    463	lcd->lcd_pd = dev_get_platdata(&spi->dev);
    464	if (!lcd->lcd_pd) {
    465		dev_err(&spi->dev, "platform data is NULL\n");
    466		return -EINVAL;
    467	}
    468
    469	ld = devm_lcd_device_register(&spi->dev, "ams369fg06", &spi->dev, lcd,
    470					&ams369fg06_lcd_ops);
    471	if (IS_ERR(ld))
    472		return PTR_ERR(ld);
    473
    474	lcd->ld = ld;
    475
    476	memset(&props, 0, sizeof(struct backlight_properties));
    477	props.type = BACKLIGHT_RAW;
    478	props.max_brightness = MAX_BRIGHTNESS;
    479
    480	bd = devm_backlight_device_register(&spi->dev, "ams369fg06-bl",
    481					&spi->dev, lcd,
    482					&ams369fg06_backlight_ops, &props);
    483	if (IS_ERR(bd))
    484		return PTR_ERR(bd);
    485
    486	bd->props.brightness = DEFAULT_BRIGHTNESS;
    487	lcd->bd = bd;
    488
    489	if (!lcd->lcd_pd->lcd_enabled) {
    490		/*
    491		 * if lcd panel was off from bootloader then
    492		 * current lcd status is powerdown and then
    493		 * it enables lcd panel.
    494		 */
    495		lcd->power = FB_BLANK_POWERDOWN;
    496
    497		ams369fg06_power(lcd, FB_BLANK_UNBLANK);
    498	} else {
    499		lcd->power = FB_BLANK_UNBLANK;
    500	}
    501
    502	spi_set_drvdata(spi, lcd);
    503
    504	dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
    505
    506	return 0;
    507}
    508
    509static void ams369fg06_remove(struct spi_device *spi)
    510{
    511	struct ams369fg06 *lcd = spi_get_drvdata(spi);
    512
    513	ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
    514}
    515
    516#ifdef CONFIG_PM_SLEEP
    517static int ams369fg06_suspend(struct device *dev)
    518{
    519	struct ams369fg06 *lcd = dev_get_drvdata(dev);
    520
    521	dev_dbg(dev, "lcd->power = %d\n", lcd->power);
    522
    523	/*
    524	 * when lcd panel is suspend, lcd panel becomes off
    525	 * regardless of status.
    526	 */
    527	return ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
    528}
    529
    530static int ams369fg06_resume(struct device *dev)
    531{
    532	struct ams369fg06 *lcd = dev_get_drvdata(dev);
    533
    534	lcd->power = FB_BLANK_POWERDOWN;
    535
    536	return ams369fg06_power(lcd, FB_BLANK_UNBLANK);
    537}
    538#endif
    539
    540static SIMPLE_DEV_PM_OPS(ams369fg06_pm_ops, ams369fg06_suspend,
    541			ams369fg06_resume);
    542
    543static void ams369fg06_shutdown(struct spi_device *spi)
    544{
    545	struct ams369fg06 *lcd = spi_get_drvdata(spi);
    546
    547	ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
    548}
    549
    550static struct spi_driver ams369fg06_driver = {
    551	.driver = {
    552		.name	= "ams369fg06",
    553		.pm	= &ams369fg06_pm_ops,
    554	},
    555	.probe		= ams369fg06_probe,
    556	.remove		= ams369fg06_remove,
    557	.shutdown	= ams369fg06_shutdown,
    558};
    559
    560module_spi_driver(ams369fg06_driver);
    561
    562MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
    563MODULE_DESCRIPTION("ams369fg06 LCD Driver");
    564MODULE_LICENSE("GPL");