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