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

panel-samsung-s6e63m0.c (21218B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * S6E63M0 AMOLED LCD drm_panel driver.
      4 *
      5 * Copyright (C) 2019 Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
      6 * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
      7 *
      8 * Andrzej Hajda <a.hajda@samsung.com>
      9 */
     10
     11#include <drm/drm_modes.h>
     12#include <drm/drm_panel.h>
     13
     14#include <linux/backlight.h>
     15#include <linux/delay.h>
     16#include <linux/gpio/consumer.h>
     17#include <linux/module.h>
     18#include <linux/regulator/consumer.h>
     19#include <linux/media-bus-format.h>
     20
     21#include <video/mipi_display.h>
     22
     23#include "panel-samsung-s6e63m0.h"
     24
     25#define S6E63M0_LCD_ID_VALUE_M2		0xA4
     26#define S6E63M0_LCD_ID_VALUE_SM2	0xB4
     27#define S6E63M0_LCD_ID_VALUE_SM2_1	0xB6
     28
     29#define NUM_GAMMA_LEVELS	28
     30#define GAMMA_TABLE_COUNT	23
     31
     32#define MAX_BRIGHTNESS		(NUM_GAMMA_LEVELS - 1)
     33
     34/* array of gamma tables for gamma value 2.2 */
     35static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
     36	/* 30 cd */
     37	{ MCS_PGAMMACTL, 0x02,
     38	  0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE,
     39	  0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD,
     40	  0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, },
     41	/* 40 cd */
     42	{ MCS_PGAMMACTL, 0x02,
     43	  0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC,
     44	  0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC,
     45	  0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, },
     46	/* 50 cd */
     47	{ MCS_PGAMMACTL, 0x02,
     48	  0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB,
     49	  0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9,
     50	  0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, },
     51	/* 60 cd */
     52	{ MCS_PGAMMACTL, 0x02,
     53	  0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9,
     54	  0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7,
     55	  0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, },
     56	/* 70 cd */
     57	{ MCS_PGAMMACTL, 0x02,
     58	  0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7,
     59	  0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7,
     60	  0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, },
     61	/* 80 cd */
     62	{ MCS_PGAMMACTL, 0x02,
     63	  0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9,
     64	  0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5,
     65	  0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, },
     66	/* 90 cd */
     67	{ MCS_PGAMMACTL, 0x02,
     68	  0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7,
     69	  0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5,
     70	  0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, },
     71	/* 100 cd */
     72	{ MCS_PGAMMACTL, 0x02,
     73	  0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6,
     74	  0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4,
     75	  0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, },
     76	/* 110 cd */
     77	{ MCS_PGAMMACTL, 0x02,
     78	  0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5,
     79	  0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4,
     80	  0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, },
     81	/* 120 cd */
     82	{ MCS_PGAMMACTL, 0x02,
     83	  0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4,
     84	  0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2,
     85	  0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, },
     86	/* 130 cd */
     87	{ MCS_PGAMMACTL, 0x02,
     88	  0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3,
     89	  0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2,
     90	  0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, },
     91	/* 140 cd */
     92	{ MCS_PGAMMACTL, 0x02,
     93	  0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1,
     94	  0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0,
     95	  0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, },
     96	/* 150 cd */
     97	{ MCS_PGAMMACTL, 0x02,
     98	  0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1,
     99	  0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0,
    100	  0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, },
    101	/* 160 cd */
    102	{ MCS_PGAMMACTL, 0x02,
    103	  0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0,
    104	  0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF,
    105	  0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, },
    106	/* 170 cd */
    107	{ MCS_PGAMMACTL, 0x02,
    108	  0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0,
    109	  0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF,
    110	  0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, },
    111	/* 180 cd */
    112	{ MCS_PGAMMACTL, 0x02,
    113	  0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0,
    114	  0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE,
    115	  0xBF,	0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, },
    116	/* 190 cd */
    117	{ MCS_PGAMMACTL, 0x02,
    118	  0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1,
    119	  0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE,
    120	  0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, },
    121	/* 200 cd */
    122	{ MCS_PGAMMACTL, 0x02,
    123	  0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1,
    124	  0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD,
    125	  0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, },
    126	/* 210 cd */
    127	{ MCS_PGAMMACTL, 0x02,
    128	  0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0,
    129	  0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC,
    130	  0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, },
    131	/* 220 cd */
    132	{ MCS_PGAMMACTL, 0x02,
    133	  0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF,
    134	  0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC,
    135	  0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, },
    136	/* 230 cd */
    137	{ MCS_PGAMMACTL, 0x02,
    138	  0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF,
    139	  0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB,
    140	  0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, },
    141	/* 240 cd */
    142	{ MCS_PGAMMACTL, 0x02,
    143	  0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0,
    144	  0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB,
    145	  0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, },
    146	/* 250 cd */
    147	{ MCS_PGAMMACTL, 0x02,
    148	  0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF,
    149	  0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA,
    150	  0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, },
    151	/* 260 cd */
    152	{ MCS_PGAMMACTL, 0x02,
    153	  0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD,
    154	  0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9,
    155	  0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, },
    156	/* 270 cd */
    157	{ MCS_PGAMMACTL, 0x02,
    158	  0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD,
    159	  0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9,
    160	  0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, },
    161	/* 280 cd */
    162	{ MCS_PGAMMACTL, 0x02,
    163	  0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE,
    164	  0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8,
    165	  0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, },
    166	/* 290 cd */
    167	{ MCS_PGAMMACTL, 0x02,
    168	  0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD,
    169	  0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7,
    170	  0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, },
    171	/* 300 cd */
    172	{ MCS_PGAMMACTL, 0x02,
    173	  0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC,
    174	  0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7,
    175	  0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, },
    176};
    177
    178#define NUM_ACL_LEVELS 7
    179#define ACL_TABLE_COUNT 28
    180
    181static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
    182	/* NULL ACL */
    183	{ MCS_BCMODE,
    184	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    185	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    186	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    187	  0x00, 0x00, 0x00 },
    188	/* 40P ACL */
    189	{ MCS_BCMODE,
    190	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
    191	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
    192	  0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26,
    193	  0x2B, 0x31, 0x36 },
    194	/* 43P ACL */
    195	{ MCS_BCMODE,
    196	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
    197	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
    198	  0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29,
    199	  0x2F, 0x34, 0x3A },
    200	/* 45P ACL */
    201	{ MCS_BCMODE,
    202	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
    203	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
    204	  0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B,
    205	  0x31, 0x37, 0x3D },
    206	/* 47P ACL */
    207	{ MCS_BCMODE,
    208	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
    209	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
    210	  0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E,
    211	  0x34, 0x3B, 0x41 },
    212	/* 48P ACL */
    213	{ MCS_BCMODE,
    214	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
    215	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
    216	  0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F,
    217	  0x36, 0x3C, 0x43 },
    218	/* 50P ACL */
    219	{ MCS_BCMODE,
    220	  0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
    221	  0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
    222	  0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31,
    223	  0x38, 0x3F, 0x46 },
    224};
    225
    226/* This tells us which ACL level goes with which gamma */
    227static u8 const s6e63m0_acl_per_gamma[NUM_GAMMA_LEVELS] = {
    228	/* 30 - 60 cd: ACL off/NULL */
    229	0, 0, 0, 0,
    230	/* 70 - 250 cd: 40P ACL */
    231	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    232	/* 260 - 300 cd: 50P ACL */
    233	6, 6, 6, 6, 6,
    234};
    235
    236/* The ELVSS backlight regulator has 5 levels */
    237#define S6E63M0_ELVSS_LEVELS 5
    238
    239static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
    240	0x00,   /* not set */
    241	0x0D,   /* 30 cd - 100 cd */
    242	0x09,   /* 110 cd - 160 cd */
    243	0x07,   /* 170 cd - 200 cd */
    244	0x00,   /* 210 cd - 300 cd */
    245};
    246
    247/* This tells us which ELVSS level goes with which gamma */
    248static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
    249	/* 30 - 100 cd */
    250	1, 1, 1, 1, 1, 1, 1, 1,
    251	/* 110 - 160 cd */
    252	2, 2, 2, 2, 2, 2,
    253	/* 170 - 200 cd */
    254	3, 3, 3, 3,
    255	/* 210 - 300 cd */
    256	4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
    257};
    258
    259struct s6e63m0 {
    260	struct device *dev;
    261	void *transport_data;
    262	int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val);
    263	int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len);
    264	struct drm_panel panel;
    265	struct backlight_device *bl_dev;
    266	u8 lcd_type;
    267	u8 elvss_pulse;
    268	bool dsi_mode;
    269
    270	struct regulator_bulk_data supplies[2];
    271	struct gpio_desc *reset_gpio;
    272
    273	bool prepared;
    274	bool enabled;
    275
    276	/*
    277	 * This field is tested by functions directly accessing bus before
    278	 * transfer, transfer is skipped if it is set. In case of transfer
    279	 * failure or unexpected response the field is set to error value.
    280	 * Such construct allows to eliminate many checks in higher level
    281	 * functions.
    282	 */
    283	int error;
    284};
    285
    286static const struct drm_display_mode default_mode = {
    287	.clock		= 25628,
    288	.hdisplay	= 480,
    289	.hsync_start	= 480 + 16,
    290	.hsync_end	= 480 + 16 + 2,
    291	.htotal		= 480 + 16 + 2 + 16,
    292	.vdisplay	= 800,
    293	.vsync_start	= 800 + 28,
    294	.vsync_end	= 800 + 28 + 2,
    295	.vtotal		= 800 + 28 + 2 + 1,
    296	.width_mm	= 53,
    297	.height_mm	= 89,
    298	.flags		= DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
    299};
    300
    301static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
    302{
    303	return container_of(panel, struct s6e63m0, panel);
    304}
    305
    306static int s6e63m0_clear_error(struct s6e63m0 *ctx)
    307{
    308	int ret = ctx->error;
    309
    310	ctx->error = 0;
    311	return ret;
    312}
    313
    314static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
    315{
    316	if (ctx->error < 0)
    317		return;
    318
    319	ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);
    320}
    321
    322static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
    323{
    324	if (ctx->error < 0 || len == 0)
    325		return;
    326
    327	ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);
    328}
    329
    330#define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
    331	({ \
    332		static const u8 d[] = { seq }; \
    333		s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
    334	})
    335
    336static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
    337{
    338	u8 id1, id2, id3;
    339	int ret;
    340
    341	s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
    342	s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
    343	s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
    344
    345	ret = s6e63m0_clear_error(ctx);
    346	if (ret) {
    347		dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
    348		ctx->lcd_type = 0x00;
    349		return ret;
    350	}
    351
    352	dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
    353
    354	/*
    355	 * We attempt to detect what panel is mounted on the controller.
    356	 * The third ID byte represents the desired ELVSS pulse for
    357	 * some displays.
    358	 */
    359	switch (id2) {
    360	case S6E63M0_LCD_ID_VALUE_M2:
    361		dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
    362		ctx->elvss_pulse = id3;
    363		break;
    364	case S6E63M0_LCD_ID_VALUE_SM2:
    365	case S6E63M0_LCD_ID_VALUE_SM2_1:
    366		dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
    367		ctx->elvss_pulse = id3;
    368		break;
    369	default:
    370		dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
    371		/* Default ELVSS pulse level */
    372		ctx->elvss_pulse = 0x16;
    373		break;
    374	}
    375
    376	ctx->lcd_type = id2;
    377
    378	return 0;
    379}
    380
    381static void s6e63m0_init(struct s6e63m0 *ctx)
    382{
    383	/*
    384	 * We do not know why there is a difference in the DSI mode.
    385	 * (No datasheet.)
    386	 *
    387	 * In the vendor driver this sequence is called
    388	 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
    389	 */
    390	if (ctx->dsi_mode)
    391		s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
    392					     0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3,
    393					     0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00);
    394	else
    395		s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
    396					     0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
    397					     0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
    398
    399	s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
    400				     0x02, 0x03, 0x1c, 0x10, 0x10);
    401	s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
    402				     0x03, 0x00, 0x00);
    403
    404	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
    405				     0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
    406				     0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
    407				     0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
    408				     0xd6);
    409	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
    410				     0x01);
    411
    412	s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
    413				     0x00, 0x8e, 0x07);
    414	s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
    415
    416	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED,
    417				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
    418				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
    419				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
    420				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
    421				     0x21, 0x20, 0x1e, 0x1e);
    422
    423	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED,
    424				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
    425				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
    426				     0x66, 0x66);
    427
    428	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN,
    429				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
    430				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
    431				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
    432				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
    433				     0x21, 0x20, 0x1e, 0x1e);
    434
    435	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN,
    436				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
    437				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
    438				     0x66, 0x66);
    439
    440	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE,
    441				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
    442				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
    443				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
    444				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
    445				     0x21, 0x20, 0x1e, 0x1e);
    446
    447	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE,
    448				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
    449				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
    450				     0x66, 0x66);
    451
    452	s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
    453				     0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
    454				     0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
    455				     0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
    456				     0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
    457
    458	s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
    459				     0x10, 0x10, 0x0b, 0x05);
    460
    461	s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
    462				     0x01);
    463
    464	s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
    465				     0x0b);
    466}
    467
    468static int s6e63m0_power_on(struct s6e63m0 *ctx)
    469{
    470	int ret;
    471
    472	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
    473	if (ret < 0)
    474		return ret;
    475
    476	msleep(25);
    477
    478	/* Be sure to send a reset pulse */
    479	gpiod_set_value(ctx->reset_gpio, 1);
    480	msleep(5);
    481	gpiod_set_value(ctx->reset_gpio, 0);
    482	msleep(120);
    483
    484	return 0;
    485}
    486
    487static int s6e63m0_power_off(struct s6e63m0 *ctx)
    488{
    489	int ret;
    490
    491	gpiod_set_value(ctx->reset_gpio, 1);
    492	msleep(120);
    493
    494	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
    495	if (ret < 0)
    496		return ret;
    497
    498	return 0;
    499}
    500
    501static int s6e63m0_disable(struct drm_panel *panel)
    502{
    503	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
    504
    505	if (!ctx->enabled)
    506		return 0;
    507
    508	backlight_disable(ctx->bl_dev);
    509
    510	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
    511	msleep(10);
    512	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
    513	msleep(120);
    514
    515	ctx->enabled = false;
    516
    517	return 0;
    518}
    519
    520static int s6e63m0_unprepare(struct drm_panel *panel)
    521{
    522	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
    523	int ret;
    524
    525	if (!ctx->prepared)
    526		return 0;
    527
    528	s6e63m0_clear_error(ctx);
    529
    530	ret = s6e63m0_power_off(ctx);
    531	if (ret < 0)
    532		return ret;
    533
    534	ctx->prepared = false;
    535
    536	return 0;
    537}
    538
    539static int s6e63m0_prepare(struct drm_panel *panel)
    540{
    541	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
    542	int ret;
    543
    544	if (ctx->prepared)
    545		return 0;
    546
    547	ret = s6e63m0_power_on(ctx);
    548	if (ret < 0)
    549		return ret;
    550
    551	/* Magic to unlock level 2 control of the display */
    552	s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
    553	/* Magic to unlock MTP reading */
    554	s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
    555
    556	ret = s6e63m0_check_lcd_type(ctx);
    557	if (ret < 0)
    558		return ret;
    559
    560	s6e63m0_init(ctx);
    561
    562	ret = s6e63m0_clear_error(ctx);
    563
    564	if (ret < 0)
    565		s6e63m0_unprepare(panel);
    566
    567	ctx->prepared = true;
    568
    569	return ret;
    570}
    571
    572static int s6e63m0_enable(struct drm_panel *panel)
    573{
    574	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
    575
    576	if (ctx->enabled)
    577		return 0;
    578
    579	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
    580	msleep(120);
    581	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
    582	msleep(10);
    583
    584	s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
    585				     0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
    586				     0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
    587				     0x0F, 0x00);
    588
    589	backlight_enable(ctx->bl_dev);
    590
    591	ctx->enabled = true;
    592
    593	return 0;
    594}
    595
    596static int s6e63m0_get_modes(struct drm_panel *panel,
    597			     struct drm_connector *connector)
    598{
    599	struct drm_display_mode *mode;
    600	static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
    601
    602	mode = drm_mode_duplicate(connector->dev, &default_mode);
    603	if (!mode) {
    604		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
    605			default_mode.hdisplay, default_mode.vdisplay,
    606			drm_mode_vrefresh(&default_mode));
    607		return -ENOMEM;
    608	}
    609
    610	connector->display_info.width_mm = mode->width_mm;
    611	connector->display_info.height_mm = mode->height_mm;
    612	drm_display_info_set_bus_formats(&connector->display_info,
    613					 &bus_format, 1);
    614	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
    615		DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
    616
    617	drm_mode_set_name(mode);
    618
    619	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
    620	drm_mode_probed_add(connector, mode);
    621
    622	return 1;
    623}
    624
    625static const struct drm_panel_funcs s6e63m0_drm_funcs = {
    626	.disable	= s6e63m0_disable,
    627	.unprepare	= s6e63m0_unprepare,
    628	.prepare	= s6e63m0_prepare,
    629	.enable		= s6e63m0_enable,
    630	.get_modes	= s6e63m0_get_modes,
    631};
    632
    633static int s6e63m0_set_brightness(struct backlight_device *bd)
    634{
    635	struct s6e63m0 *ctx = bl_get_data(bd);
    636	int brightness = bd->props.brightness;
    637	u8 elvss_val;
    638	u8 elvss_cmd_set[5];
    639	int i;
    640
    641	/* Adjust ELVSS to candela level */
    642	i = s6e63m0_elvss_per_gamma[brightness];
    643	elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];
    644	if (elvss_val > 0x1f)
    645		elvss_val = 0x1f;
    646	elvss_cmd_set[0] = MCS_TEMP_SWIRE;
    647	elvss_cmd_set[1] = elvss_val;
    648	elvss_cmd_set[2] = elvss_val;
    649	elvss_cmd_set[3] = elvss_val;
    650	elvss_cmd_set[4] = elvss_val;
    651	s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);
    652
    653	/* Update the ACL per gamma value */
    654	i = s6e63m0_acl_per_gamma[brightness];
    655	s6e63m0_dcs_write(ctx, s6e63m0_acl[i],
    656			  ARRAY_SIZE(s6e63m0_acl[i]));
    657
    658	/* Update gamma table */
    659	s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
    660			  ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
    661	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);
    662
    663
    664	return s6e63m0_clear_error(ctx);
    665}
    666
    667static const struct backlight_ops s6e63m0_backlight_ops = {
    668	.update_status	= s6e63m0_set_brightness,
    669};
    670
    671static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
    672{
    673	struct backlight_properties props = {
    674		.type		= BACKLIGHT_RAW,
    675		.brightness	= max_brightness,
    676		.max_brightness = max_brightness,
    677	};
    678	struct device *dev = ctx->dev;
    679	int ret = 0;
    680
    681	ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
    682						     &s6e63m0_backlight_ops,
    683						     &props);
    684	if (IS_ERR(ctx->bl_dev)) {
    685		ret = PTR_ERR(ctx->bl_dev);
    686		dev_err(dev, "error registering backlight device (%d)\n", ret);
    687	}
    688
    689	return ret;
    690}
    691
    692int s6e63m0_probe(struct device *dev, void *trsp,
    693		  int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val),
    694		  int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len),
    695		  bool dsi_mode)
    696{
    697	struct s6e63m0 *ctx;
    698	u32 max_brightness;
    699	int ret;
    700
    701	ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
    702	if (!ctx)
    703		return -ENOMEM;
    704
    705	ctx->transport_data = trsp;
    706	ctx->dsi_mode = dsi_mode;
    707	ctx->dcs_read = dcs_read;
    708	ctx->dcs_write = dcs_write;
    709	dev_set_drvdata(dev, ctx);
    710
    711	ctx->dev = dev;
    712	ctx->enabled = false;
    713	ctx->prepared = false;
    714
    715	ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
    716	if (ret)
    717		max_brightness = MAX_BRIGHTNESS;
    718	if (max_brightness > MAX_BRIGHTNESS) {
    719		dev_err(dev, "illegal max brightness specified\n");
    720		max_brightness = MAX_BRIGHTNESS;
    721	}
    722
    723	ctx->supplies[0].supply = "vdd3";
    724	ctx->supplies[1].supply = "vci";
    725	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
    726				      ctx->supplies);
    727	if (ret < 0) {
    728		dev_err(dev, "failed to get regulators: %d\n", ret);
    729		return ret;
    730	}
    731
    732	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
    733	if (IS_ERR(ctx->reset_gpio)) {
    734		dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
    735		return PTR_ERR(ctx->reset_gpio);
    736	}
    737
    738	drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
    739		       dsi_mode ? DRM_MODE_CONNECTOR_DSI :
    740		       DRM_MODE_CONNECTOR_DPI);
    741
    742	ret = s6e63m0_backlight_register(ctx, max_brightness);
    743	if (ret < 0)
    744		return ret;
    745
    746	drm_panel_add(&ctx->panel);
    747
    748	return 0;
    749}
    750EXPORT_SYMBOL_GPL(s6e63m0_probe);
    751
    752void s6e63m0_remove(struct device *dev)
    753{
    754	struct s6e63m0 *ctx = dev_get_drvdata(dev);
    755
    756	drm_panel_remove(&ctx->panel);
    757}
    758EXPORT_SYMBOL_GPL(s6e63m0_remove);
    759
    760MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
    761MODULE_DESCRIPTION("s6e63m0 LCD Driver");
    762MODULE_LICENSE("GPL v2");