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	/*
274	 * This field is tested by functions directly accessing bus before
275	 * transfer, transfer is skipped if it is set. In case of transfer
276	 * failure or unexpected response the field is set to error value.
277	 * Such construct allows to eliminate many checks in higher level
278	 * 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 for
354	 * 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 called
385	 * "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	else
392		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