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 bool prepared;
274 bool enabled;
275
276 /*
277 * This field is tested by functions directly accessing bus before
278 * transfer, transfer is skipped if it is set. In case of transfer
279 * failure or unexpected response the field is set to error value.
280 * Such construct allows to eliminate many checks in higher level
281 * functions.
282 */
283 int error;
284 };
285
286 static const struct drm_display_mode default_mode = {
287 .clock = 25628,
288 .hdisplay = 480,
289 .hsync_start = 480 + 16,
290 .hsync_end = 480 + 16 + 2,
291 .htotal = 480 + 16 + 2 + 16,
292 .vdisplay = 800,
293 .vsync_start = 800 + 28,
294 .vsync_end = 800 + 28 + 2,
295 .vtotal = 800 + 28 + 2 + 1,
296 .width_mm = 53,
297 .height_mm = 89,
298 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
299 };
300
panel_to_s6e63m0(struct drm_panel * panel)301 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
302 {
303 return container_of(panel, struct s6e63m0, panel);
304 }
305
s6e63m0_clear_error(struct s6e63m0 * ctx)306 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
307 {
308 int ret = ctx->error;
309
310 ctx->error = 0;
311 return ret;
312 }
313
s6e63m0_dcs_read(struct s6e63m0 * ctx,const u8 cmd,u8 * data)314 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
315 {
316 if (ctx->error < 0)
317 return;
318
319 ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);
320 }
321
s6e63m0_dcs_write(struct s6e63m0 * ctx,const u8 * data,size_t len)322 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
323 {
324 if (ctx->error < 0 || len == 0)
325 return;
326
327 ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);
328 }
329
330 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
331 ({ \
332 static const u8 d[] = { seq }; \
333 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
334 })
335
s6e63m0_check_lcd_type(struct s6e63m0 * ctx)336 static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
337 {
338 u8 id1, id2, id3;
339 int ret;
340
341 s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
342 s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
343 s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
344
345 ret = s6e63m0_clear_error(ctx);
346 if (ret) {
347 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
348 ctx->lcd_type = 0x00;
349 return ret;
350 }
351
352 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
353
354 /*
355 * We attempt to detect what panel is mounted on the controller.
356 * The third ID byte represents the desired ELVSS pulse for
357 * some displays.
358 */
359 switch (id2) {
360 case S6E63M0_LCD_ID_VALUE_M2:
361 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
362 ctx->elvss_pulse = id3;
363 break;
364 case S6E63M0_LCD_ID_VALUE_SM2:
365 case S6E63M0_LCD_ID_VALUE_SM2_1:
366 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
367 ctx->elvss_pulse = id3;
368 break;
369 default:
370 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
371 /* Default ELVSS pulse level */
372 ctx->elvss_pulse = 0x16;
373 break;
374 }
375
376 ctx->lcd_type = id2;
377
378 return 0;
379 }
380
s6e63m0_init(struct s6e63m0 * ctx)381 static void s6e63m0_init(struct s6e63m0 *ctx)
382 {
383 /*
384 * We do not know why there is a difference in the DSI mode.
385 * (No datasheet.)
386 *
387 * In the vendor driver this sequence is called
388 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
389 */
390 if (ctx->dsi_mode)
391 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
392 0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3,
393 0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00);
394 else
395 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
396 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
397 0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
398
399 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
400 0x02, 0x03, 0x1c, 0x10, 0x10);
401 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
402 0x03, 0x00, 0x00);
403
404 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
405 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
406 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
407 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
408 0xd6);
409 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
410 0x01);
411
412 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
413 0x00, 0x8e, 0x07);
414 s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
415
416 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED,
417 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
418 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
419 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
420 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
421 0x21, 0x20, 0x1e, 0x1e);
422
423 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED,
424 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
425 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
426 0x66, 0x66);
427
428 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN,
429 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
430 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
431 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
432 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
433 0x21, 0x20, 0x1e, 0x1e);
434
435 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN,
436 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
437 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
438 0x66, 0x66);
439
440 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE,
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_BLUE,
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_BCMODE,
453 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
454 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
455 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
456 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
457
458 s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
459 0x10, 0x10, 0x0b, 0x05);
460
461 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
462 0x01);
463
464 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
465 0x0b);
466 }
467
s6e63m0_power_on(struct s6e63m0 * ctx)468 static int s6e63m0_power_on(struct s6e63m0 *ctx)
469 {
470 int ret;
471
472 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
473 if (ret < 0)
474 return ret;
475
476 msleep(25);
477
478 /* Be sure to send a reset pulse */
479 gpiod_set_value(ctx->reset_gpio, 1);
480 msleep(5);
481 gpiod_set_value(ctx->reset_gpio, 0);
482 msleep(120);
483
484 return 0;
485 }
486
s6e63m0_power_off(struct s6e63m0 * ctx)487 static int s6e63m0_power_off(struct s6e63m0 *ctx)
488 {
489 int ret;
490
491 gpiod_set_value(ctx->reset_gpio, 1);
492 msleep(120);
493
494 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
495 if (ret < 0)
496 return ret;
497
498 return 0;
499 }
500
s6e63m0_disable(struct drm_panel * panel)501 static int s6e63m0_disable(struct drm_panel *panel)
502 {
503 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
504
505 if (!ctx->enabled)
506 return 0;
507
508 backlight_disable(ctx->bl_dev);
509
510 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
511 msleep(10);
512 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
513 msleep(120);
514
515 ctx->enabled = false;
516
517 return 0;
518 }
519
s6e63m0_unprepare(struct drm_panel * panel)520 static int s6e63m0_unprepare(struct drm_panel *panel)
521 {
522 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
523 int ret;
524
525 if (!ctx->prepared)
526 return 0;
527
528 s6e63m0_clear_error(ctx);
529
530 ret = s6e63m0_power_off(ctx);
531 if (ret < 0)
532 return ret;
533
534 ctx->prepared = false;
535
536 return 0;
537 }
538
s6e63m0_prepare(struct drm_panel * panel)539 static int s6e63m0_prepare(struct drm_panel *panel)
540 {
541 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
542 int ret;
543
544 if (ctx->prepared)
545 return 0;
546
547 ret = s6e63m0_power_on(ctx);
548 if (ret < 0)
549 return ret;
550
551 /* Magic to unlock level 2 control of the display */
552 s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
553 /* Magic to unlock MTP reading */
554 s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
555
556 ret = s6e63m0_check_lcd_type(ctx);
557 if (ret < 0)
558 return ret;
559
560 s6e63m0_init(ctx);
561
562 ret = s6e63m0_clear_error(ctx);
563
564 if (ret < 0)
565 s6e63m0_unprepare(panel);
566
567 ctx->prepared = true;
568
569 return ret;
570 }
571
s6e63m0_enable(struct drm_panel * panel)572 static int s6e63m0_enable(struct drm_panel *panel)
573 {
574 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
575
576 if (ctx->enabled)
577 return 0;
578
579 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
580 msleep(120);
581 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
582 msleep(10);
583
584 s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
585 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
586 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
587 0x0F, 0x00);
588
589 backlight_enable(ctx->bl_dev);
590
591 ctx->enabled = true;
592
593 return 0;
594 }
595
s6e63m0_get_modes(struct drm_panel * panel,struct drm_connector * connector)596 static int s6e63m0_get_modes(struct drm_panel *panel,
597 struct drm_connector *connector)
598 {
599 struct drm_display_mode *mode;
600 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
601
602 mode = drm_mode_duplicate(connector->dev, &default_mode);
603 if (!mode) {
604 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
605 default_mode.hdisplay, default_mode.vdisplay,
606 drm_mode_vrefresh(&default_mode));
607 return -ENOMEM;
608 }
609
610 connector->display_info.width_mm = mode->width_mm;
611 connector->display_info.height_mm = mode->height_mm;
612 drm_display_info_set_bus_formats(&connector->display_info,
613 &bus_format, 1);
614 connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
615 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
616
617 drm_mode_set_name(mode);
618
619 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
620 drm_mode_probed_add(connector, mode);
621
622 return 1;
623 }
624
625 static const struct drm_panel_funcs s6e63m0_drm_funcs = {
626 .disable = s6e63m0_disable,
627 .unprepare = s6e63m0_unprepare,
628 .prepare = s6e63m0_prepare,
629 .enable = s6e63m0_enable,
630 .get_modes = s6e63m0_get_modes,
631 };
632
s6e63m0_set_brightness(struct backlight_device * bd)633 static int s6e63m0_set_brightness(struct backlight_device *bd)
634 {
635 struct s6e63m0 *ctx = bl_get_data(bd);
636 int brightness = bd->props.brightness;
637 u8 elvss_val;
638 u8 elvss_cmd_set[5];
639 int i;
640
641 /* Adjust ELVSS to candela level */
642 i = s6e63m0_elvss_per_gamma[brightness];
643 elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];
644 if (elvss_val > 0x1f)
645 elvss_val = 0x1f;
646 elvss_cmd_set[0] = MCS_TEMP_SWIRE;
647 elvss_cmd_set[1] = elvss_val;
648 elvss_cmd_set[2] = elvss_val;
649 elvss_cmd_set[3] = elvss_val;
650 elvss_cmd_set[4] = elvss_val;
651 s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);
652
653 /* Update the ACL per gamma value */
654 i = s6e63m0_acl_per_gamma[brightness];
655 s6e63m0_dcs_write(ctx, s6e63m0_acl[i],
656 ARRAY_SIZE(s6e63m0_acl[i]));
657
658 /* Update gamma table */
659 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
660 ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
661 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);
662
663
664 return s6e63m0_clear_error(ctx);
665 }
666
667 static const struct backlight_ops s6e63m0_backlight_ops = {
668 .update_status = s6e63m0_set_brightness,
669 };
670
s6e63m0_backlight_register(struct s6e63m0 * ctx,u32 max_brightness)671 static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
672 {
673 struct backlight_properties props = {
674 .type = BACKLIGHT_RAW,
675 .brightness = max_brightness,
676 .max_brightness = max_brightness,
677 };
678 struct device *dev = ctx->dev;
679 int ret = 0;
680
681 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
682 &s6e63m0_backlight_ops,
683 &props);
684 if (IS_ERR(ctx->bl_dev)) {
685 ret = PTR_ERR(ctx->bl_dev);
686 dev_err(dev, "error registering backlight device (%d)\n", ret);
687 }
688
689 return ret;
690 }
691
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)692 int s6e63m0_probe(struct device *dev, void *trsp,
693 int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val),
694 int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len),
695 bool dsi_mode)
696 {
697 struct s6e63m0 *ctx;
698 u32 max_brightness;
699 int ret;
700
701 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
702 if (!ctx)
703 return -ENOMEM;
704
705 ctx->transport_data = trsp;
706 ctx->dsi_mode = dsi_mode;
707 ctx->dcs_read = dcs_read;
708 ctx->dcs_write = dcs_write;
709 dev_set_drvdata(dev, ctx);
710
711 ctx->dev = dev;
712 ctx->enabled = false;
713 ctx->prepared = false;
714
715 ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
716 if (ret)
717 max_brightness = MAX_BRIGHTNESS;
718 if (max_brightness > MAX_BRIGHTNESS) {
719 dev_err(dev, "illegal max brightness specified\n");
720 max_brightness = MAX_BRIGHTNESS;
721 }
722
723 ctx->supplies[0].supply = "vdd3";
724 ctx->supplies[1].supply = "vci";
725 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
726 ctx->supplies);
727 if (ret < 0) {
728 dev_err(dev, "failed to get regulators: %d\n", ret);
729 return ret;
730 }
731
732 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
733 if (IS_ERR(ctx->reset_gpio)) {
734 dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
735 return PTR_ERR(ctx->reset_gpio);
736 }
737
738 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
739 dsi_mode ? DRM_MODE_CONNECTOR_DSI :
740 DRM_MODE_CONNECTOR_DPI);
741
742 ret = s6e63m0_backlight_register(ctx, max_brightness);
743 if (ret < 0)
744 return ret;
745
746 drm_panel_add(&ctx->panel);
747
748 return 0;
749 }
750 EXPORT_SYMBOL_GPL(s6e63m0_probe);
751
s6e63m0_remove(struct device * dev)752 void s6e63m0_remove(struct device *dev)
753 {
754 struct s6e63m0 *ctx = dev_get_drvdata(dev);
755
756 drm_panel_remove(&ctx->panel);
757 }
758 EXPORT_SYMBOL_GPL(s6e63m0_remove);
759
760 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
761 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
762 MODULE_LICENSE("GPL v2");
763