brintos

brintos / linux-shallow public Read only

0
0
Text · 20.4 KiB · a0e5698 Raw
738 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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.c7 *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		0xA426#define S6E63M0_LCD_ID_VALUE_SM2	0xB427#define S6E63M0_LCD_ID_VALUE_SM2_1	0xB628 29#define NUM_GAMMA_LEVELS	2830#define GAMMA_TABLE_COUNT	2331 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 7179#define ACL_TABLE_COUNT 28180 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 5238 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	/*274	 * This field is tested by functions directly accessing bus before275	 * transfer, transfer is skipped if it is set. In case of transfer276	 * failure or unexpected response the field is set to error value.277	 * Such construct allows to eliminate many checks in higher level278	 * functions.279	 */280	int error;281};282 283static const struct drm_display_mode default_mode = {284	.clock		= 25628,285	.hdisplay	= 480,286	.hsync_start	= 480 + 16,287	.hsync_end	= 480 + 16 + 2,288	.htotal		= 480 + 16 + 2 + 16,289	.vdisplay	= 800,290	.vsync_start	= 800 + 28,291	.vsync_end	= 800 + 28 + 2,292	.vtotal		= 800 + 28 + 2 + 1,293	.width_mm	= 53,294	.height_mm	= 89,295	.flags		= DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,296};297 298static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)299{300	return container_of(panel, struct s6e63m0, panel);301}302 303static int s6e63m0_clear_error(struct s6e63m0 *ctx)304{305	int ret = ctx->error;306 307	ctx->error = 0;308	return ret;309}310 311static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)312{313	if (ctx->error < 0)314		return;315 316	ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);317}318 319static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)320{321	if (ctx->error < 0 || len == 0)322		return;323 324	ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);325}326 327#define s6e63m0_dcs_write_seq_static(ctx, seq ...) \328	({ \329		static const u8 d[] = { seq }; \330		s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \331	})332 333static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)334{335	u8 id1, id2, id3;336	int ret;337 338	s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);339	s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);340	s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);341 342	ret = s6e63m0_clear_error(ctx);343	if (ret) {344		dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);345		ctx->lcd_type = 0x00;346		return ret;347	}348 349	dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);350 351	/*352	 * We attempt to detect what panel is mounted on the controller.353	 * The third ID byte represents the desired ELVSS pulse for354	 * some displays.355	 */356	switch (id2) {357	case S6E63M0_LCD_ID_VALUE_M2:358		dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");359		ctx->elvss_pulse = id3;360		break;361	case S6E63M0_LCD_ID_VALUE_SM2:362	case S6E63M0_LCD_ID_VALUE_SM2_1:363		dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");364		ctx->elvss_pulse = id3;365		break;366	default:367		dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);368		/* Default ELVSS pulse level */369		ctx->elvss_pulse = 0x16;370		break;371	}372 373	ctx->lcd_type = id2;374 375	return 0;376}377 378static void s6e63m0_init(struct s6e63m0 *ctx)379{380	/*381	 * We do not know why there is a difference in the DSI mode.382	 * (No datasheet.)383	 *384	 * In the vendor driver this sequence is called385	 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".386	 */387	if (ctx->dsi_mode)388		s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,389					     0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3,390					     0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00);391	else392		s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,393					     0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,394					     0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);395 396	s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,397				     0x02, 0x03, 0x1c, 0x10, 0x10);398	s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,399				     0x03, 0x00, 0x00);400 401	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,402				     0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,403				     0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,404				     0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,405				     0xd6);406	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,407				     0x01);408 409	s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,410				     0x00, 0x8e, 0x07);411	s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);412 413	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED,414				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,415				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,416				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,417				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,418				     0x21, 0x20, 0x1e, 0x1e);419 420	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED,421				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,422				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,423				     0x66, 0x66);424 425	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN,426				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,427				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,428				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,429				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,430				     0x21, 0x20, 0x1e, 0x1e);431 432	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN,433				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,434				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,435				     0x66, 0x66);436 437	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE,438				     0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,439				     0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,440				     0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,441				     0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,442				     0x21, 0x20, 0x1e, 0x1e);443 444	s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE,445				     0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,446				     0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,447				     0x66, 0x66);448 449	s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,450				     0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,451				     0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,452				     0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,453				     0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);454 455	s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,456				     0x10, 0x10, 0x0b, 0x05);457 458	s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,459				     0x01);460 461	s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,462				     0x0b);463}464 465static int s6e63m0_power_on(struct s6e63m0 *ctx)466{467	int ret;468 469	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);470	if (ret < 0)471		return ret;472 473	msleep(25);474 475	/* Be sure to send a reset pulse */476	gpiod_set_value(ctx->reset_gpio, 1);477	msleep(5);478	gpiod_set_value(ctx->reset_gpio, 0);479	msleep(120);480 481	return 0;482}483 484static int s6e63m0_power_off(struct s6e63m0 *ctx)485{486	int ret;487 488	gpiod_set_value(ctx->reset_gpio, 1);489	msleep(120);490 491	ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);492	if (ret < 0)493		return ret;494 495	return 0;496}497 498static int s6e63m0_disable(struct drm_panel *panel)499{500	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);501 502	backlight_disable(ctx->bl_dev);503 504	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);505	msleep(10);506	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);507	msleep(120);508 509	return 0;510}511 512static int s6e63m0_unprepare(struct drm_panel *panel)513{514	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);515	int ret;516 517	s6e63m0_clear_error(ctx);518 519	ret = s6e63m0_power_off(ctx);520	if (ret < 0)521		return ret;522 523	return 0;524}525 526static int s6e63m0_prepare(struct drm_panel *panel)527{528	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);529	int ret;530 531	ret = s6e63m0_power_on(ctx);532	if (ret < 0)533		return ret;534 535	/* Magic to unlock level 2 control of the display */536	s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);537	/* Magic to unlock MTP reading */538	s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);539 540	ret = s6e63m0_check_lcd_type(ctx);541	if (ret < 0)542		return ret;543 544	s6e63m0_init(ctx);545 546	ret = s6e63m0_clear_error(ctx);547 548	if (ret < 0)549		s6e63m0_unprepare(panel);550 551	return ret;552}553 554static int s6e63m0_enable(struct drm_panel *panel)555{556	struct s6e63m0 *ctx = panel_to_s6e63m0(panel);557 558	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);559	msleep(120);560	s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);561	msleep(10);562 563	s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,564				     0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,565				     0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,566				     0x0F, 0x00);567 568	backlight_enable(ctx->bl_dev);569 570	return 0;571}572 573static int s6e63m0_get_modes(struct drm_panel *panel,574			     struct drm_connector *connector)575{576	struct drm_display_mode *mode;577	static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;578 579	mode = drm_mode_duplicate(connector->dev, &default_mode);580	if (!mode) {581		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",582			default_mode.hdisplay, default_mode.vdisplay,583			drm_mode_vrefresh(&default_mode));584		return -ENOMEM;585	}586 587	connector->display_info.width_mm = mode->width_mm;588	connector->display_info.height_mm = mode->height_mm;589	drm_display_info_set_bus_formats(&connector->display_info,590					 &bus_format, 1);591	connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |592		DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;593 594	drm_mode_set_name(mode);595 596	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;597	drm_mode_probed_add(connector, mode);598 599	return 1;600}601 602static const struct drm_panel_funcs s6e63m0_drm_funcs = {603	.disable	= s6e63m0_disable,604	.unprepare	= s6e63m0_unprepare,605	.prepare	= s6e63m0_prepare,606	.enable		= s6e63m0_enable,607	.get_modes	= s6e63m0_get_modes,608};609 610static int s6e63m0_set_brightness(struct backlight_device *bd)611{612	struct s6e63m0 *ctx = bl_get_data(bd);613	int brightness = bd->props.brightness;614	u8 elvss_val;615	u8 elvss_cmd_set[5];616	int i;617 618	/* Adjust ELVSS to candela level */619	i = s6e63m0_elvss_per_gamma[brightness];620	elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];621	if (elvss_val > 0x1f)622		elvss_val = 0x1f;623	elvss_cmd_set[0] = MCS_TEMP_SWIRE;624	elvss_cmd_set[1] = elvss_val;625	elvss_cmd_set[2] = elvss_val;626	elvss_cmd_set[3] = elvss_val;627	elvss_cmd_set[4] = elvss_val;628	s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);629 630	/* Update the ACL per gamma value */631	i = s6e63m0_acl_per_gamma[brightness];632	s6e63m0_dcs_write(ctx, s6e63m0_acl[i],633			  ARRAY_SIZE(s6e63m0_acl[i]));634 635	/* Update gamma table */636	s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],637			  ARRAY_SIZE(s6e63m0_gamma_22[brightness]));638	s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);639 640 641	return s6e63m0_clear_error(ctx);642}643 644static const struct backlight_ops s6e63m0_backlight_ops = {645	.update_status	= s6e63m0_set_brightness,646};647 648static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)649{650	struct backlight_properties props = {651		.type		= BACKLIGHT_RAW,652		.brightness	= max_brightness,653		.max_brightness = max_brightness,654	};655	struct device *dev = ctx->dev;656	int ret = 0;657 658	ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,659						     &s6e63m0_backlight_ops,660						     &props);661	if (IS_ERR(ctx->bl_dev)) {662		ret = PTR_ERR(ctx->bl_dev);663		dev_err(dev, "error registering backlight device (%d)\n", ret);664	}665 666	return ret;667}668 669int s6e63m0_probe(struct device *dev, void *trsp,670		  int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val),671		  int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len),672		  bool dsi_mode)673{674	struct s6e63m0 *ctx;675	u32 max_brightness;676	int ret;677 678	ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);679	if (!ctx)680		return -ENOMEM;681 682	ctx->transport_data = trsp;683	ctx->dsi_mode = dsi_mode;684	ctx->dcs_read = dcs_read;685	ctx->dcs_write = dcs_write;686	dev_set_drvdata(dev, ctx);687 688	ctx->dev = dev;689 690	ret = device_property_read_u32(dev, "max-brightness", &max_brightness);691	if (ret)692		max_brightness = MAX_BRIGHTNESS;693	if (max_brightness > MAX_BRIGHTNESS) {694		dev_err(dev, "illegal max brightness specified\n");695		max_brightness = MAX_BRIGHTNESS;696	}697 698	ctx->supplies[0].supply = "vdd3";699	ctx->supplies[1].supply = "vci";700	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),701				      ctx->supplies);702	if (ret < 0) {703		dev_err(dev, "failed to get regulators: %d\n", ret);704		return ret;705	}706 707	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);708	if (IS_ERR(ctx->reset_gpio)) {709		dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));710		return PTR_ERR(ctx->reset_gpio);711	}712 713	drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,714		       dsi_mode ? DRM_MODE_CONNECTOR_DSI :715		       DRM_MODE_CONNECTOR_DPI);716 717	ret = s6e63m0_backlight_register(ctx, max_brightness);718	if (ret < 0)719		return ret;720 721	drm_panel_add(&ctx->panel);722 723	return 0;724}725EXPORT_SYMBOL_GPL(s6e63m0_probe);726 727void s6e63m0_remove(struct device *dev)728{729	struct s6e63m0 *ctx = dev_get_drvdata(dev);730 731	drm_panel_remove(&ctx->panel);732}733EXPORT_SYMBOL_GPL(s6e63m0_remove);734 735MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");736MODULE_DESCRIPTION("s6e63m0 LCD Driver");737MODULE_LICENSE("GPL v2");738