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 */
35 static 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 
181 static 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 */
227 static 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 
239 static 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 */
248 static 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 
259 struct 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 
286 static 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 
301 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
302 {
303 	return container_of(panel, struct s6e63m0, panel);
304 }
305 
306 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
307 {
308 	int ret = ctx->error;
309 
310 	ctx->error = 0;
311 	return ret;
312 }
313 
314 static 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 
322 static 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 
336 static 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 
381 static 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 
468 static 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 
487 static 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 
501 static 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 
520 static 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 
539 static 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 
572 static 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 
596 static 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 
625 static 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 
633 static 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 
667 static const struct backlight_ops s6e63m0_backlight_ops = {
668 	.update_status	= s6e63m0_set_brightness,
669 };
670 
671 static 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 
692 int 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 }
750 EXPORT_SYMBOL_GPL(s6e63m0_probe);
751 
752 void s6e63m0_remove(struct device *dev)
753 {
754 	struct s6e63m0 *ctx = dev_get_drvdata(dev);
755 
756 	drm_panel_remove(&ctx->panel);
757 }
758 EXPORT_SYMBOL_GPL(s6e63m0_remove);
759 
760 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
761 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
762 MODULE_LICENSE("GPL v2");
763