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");
763