1 // SPDX-License-Identifier: GPL-2.0
3 * S6E63M0 AMOLED LCD drm_panel driver.
5 * Copyright (C) 2019 Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>
6 * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
8 * Andrzej Hajda <a.hajda@samsung.com>
11 #include <drm/drm_modes.h>
12 #include <drm/drm_panel.h>
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>
21 #include <video/mipi_display.h>
23 #include "panel-samsung-s6e63m0.h"
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
29 #define NUM_GAMMA_LEVELS 28
30 #define GAMMA_TABLE_COUNT 23
32 #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
34 /* array of gamma tables for gamma value 2.2 */
35 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
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, },
178 #define NUM_ACL_LEVELS 7
179 #define ACL_TABLE_COUNT 28
181 static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
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,
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,
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,
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,
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,
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,
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,
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 */
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 */
236 /* The ELVSS backlight regulator has 5 levels */
237 #define S6E63M0_ELVSS_LEVELS 5
239 static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
241 0x0D, /* 30 cd - 100 cd */
242 0x09, /* 110 cd - 160 cd */
243 0x07, /* 170 cd - 200 cd */
244 0x00, /* 210 cd - 300 cd */
247 /* This tells us which ELVSS level goes with which gamma */
248 static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
250 1, 1, 1, 1, 1, 1, 1, 1,
256 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
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;
270 struct regulator_bulk_data supplies[2];
271 struct gpio_desc *reset_gpio;
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
286 static const struct drm_display_mode default_mode = {
289 .hsync_start = 480 + 16,
290 .hsync_end = 480 + 16 + 2,
291 .htotal = 480 + 16 + 2 + 16,
293 .vsync_start = 800 + 28,
294 .vsync_end = 800 + 28 + 2,
295 .vtotal = 800 + 28 + 2 + 1,
298 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
301 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
303 return container_of(panel, struct s6e63m0, panel);
306 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
308 int ret = ctx->error;
314 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
319 ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);
322 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
324 if (ctx->error < 0 || len == 0)
327 ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);
330 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
332 static const u8 d[] = { seq }; \
333 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
336 static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
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);
345 ret = s6e63m0_clear_error(ctx);
347 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
348 ctx->lcd_type = 0x00;
352 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
355 * We attempt to detect what panel is mounted on the controller.
356 * The third ID byte represents the desired ELVSS pulse for
360 case S6E63M0_LCD_ID_VALUE_M2:
361 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
362 ctx->elvss_pulse = id3;
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;
370 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
371 /* Default ELVSS pulse level */
372 ctx->elvss_pulse = 0x16;
381 static void s6e63m0_init(struct s6e63m0 *ctx)
384 * We do not know why there is a difference in the DSI mode.
387 * In the vendor driver this sequence is called
388 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
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);
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);
399 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
400 0x02, 0x03, 0x1c, 0x10, 0x10);
401 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
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,
409 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
412 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
414 s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
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);
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,
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);
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,
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);
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,
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);
458 s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
459 0x10, 0x10, 0x0b, 0x05);
461 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
464 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
468 static int s6e63m0_power_on(struct s6e63m0 *ctx)
472 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
478 /* Be sure to send a reset pulse */
479 gpiod_set_value(ctx->reset_gpio, 1);
481 gpiod_set_value(ctx->reset_gpio, 0);
487 static int s6e63m0_power_off(struct s6e63m0 *ctx)
491 gpiod_set_value(ctx->reset_gpio, 1);
494 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
501 static int s6e63m0_disable(struct drm_panel *panel)
503 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
508 backlight_disable(ctx->bl_dev);
510 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
512 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
515 ctx->enabled = false;
520 static int s6e63m0_unprepare(struct drm_panel *panel)
522 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
528 s6e63m0_clear_error(ctx);
530 ret = s6e63m0_power_off(ctx);
534 ctx->prepared = false;
539 static int s6e63m0_prepare(struct drm_panel *panel)
541 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
547 ret = s6e63m0_power_on(ctx);
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);
556 ret = s6e63m0_check_lcd_type(ctx);
562 ret = s6e63m0_clear_error(ctx);
565 s6e63m0_unprepare(panel);
567 ctx->prepared = true;
572 static int s6e63m0_enable(struct drm_panel *panel)
574 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
579 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
581 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
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,
589 backlight_enable(ctx->bl_dev);
596 static int s6e63m0_get_modes(struct drm_panel *panel,
597 struct drm_connector *connector)
599 struct drm_display_mode *mode;
600 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
602 mode = drm_mode_duplicate(connector->dev, &default_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));
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,
614 connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
615 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
617 drm_mode_set_name(mode);
619 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
620 drm_mode_probed_add(connector, mode);
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,
633 static int s6e63m0_set_brightness(struct backlight_device *bd)
635 struct s6e63m0 *ctx = bl_get_data(bd);
636 int brightness = bd->props.brightness;
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)
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);
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]));
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);
664 return s6e63m0_clear_error(ctx);
667 static const struct backlight_ops s6e63m0_backlight_ops = {
668 .update_status = s6e63m0_set_brightness,
671 static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
673 struct backlight_properties props = {
674 .type = BACKLIGHT_RAW,
675 .brightness = max_brightness,
676 .max_brightness = max_brightness,
678 struct device *dev = ctx->dev;
681 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
682 &s6e63m0_backlight_ops,
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);
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),
701 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
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);
712 ctx->enabled = false;
713 ctx->prepared = false;
715 ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
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;
723 ctx->supplies[0].supply = "vdd3";
724 ctx->supplies[1].supply = "vci";
725 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
728 dev_err(dev, "failed to get regulators: %d\n", ret);
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);
738 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
739 dsi_mode ? DRM_MODE_CONNECTOR_DSI :
740 DRM_MODE_CONNECTOR_DPI);
742 ret = s6e63m0_backlight_register(ctx, max_brightness);
746 drm_panel_add(&ctx->panel);
750 EXPORT_SYMBOL_GPL(s6e63m0_probe);
752 void s6e63m0_remove(struct device *dev)
754 struct s6e63m0 *ctx = dev_get_drvdata(dev);
756 drm_panel_remove(&ctx->panel);
758 EXPORT_SYMBOL_GPL(s6e63m0_remove);
760 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
761 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
762 MODULE_LICENSE("GPL v2");