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