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