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