xref: /linux/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 	/*
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 
283 static 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 
298 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
299 {
300 	return container_of(panel, struct s6e63m0, panel);
301 }
302 
303 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
304 {
305 	int ret = ctx->error;
306 
307 	ctx->error = 0;
308 	return ret;
309 }
310 
311 static 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 
319 static 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 
333 static 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 
378 static 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 
465 static 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 
484 static 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 
498 static 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 
512 static 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 
526 static 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 
554 static 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 
573 static 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 
602 static 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 
610 static 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 
644 static const struct backlight_ops s6e63m0_backlight_ops = {
645 	.update_status	= s6e63m0_set_brightness,
646 };
647 
648 static 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 
669 int 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 }
725 EXPORT_SYMBOL_GPL(s6e63m0_probe);
726 
727 void s6e63m0_remove(struct device *dev)
728 {
729 	struct s6e63m0 *ctx = dev_get_drvdata(dev);
730 
731 	drm_panel_remove(&ctx->panel);
732 }
733 EXPORT_SYMBOL_GPL(s6e63m0_remove);
734 
735 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
736 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
737 MODULE_LICENSE("GPL v2");
738