1 // SPDX-License-Identifier: GPL-2.0
3 * S6E63M0 AMOLED LCD drm_panel driver.
6 * Derived from drivers/gpu/drm/panel-samsung-ld9040.c
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 /* Manufacturer Command Set */
26 #define MCS_ELVSS_ON 0xb1
27 #define MCS_TEMP_SWIRE 0xb2
28 #define MCS_PENTILE_1 0xb3
29 #define MCS_PENTILE_2 0xb4
30 #define MCS_GAMMA_DELTA_Y_RED 0xb5
31 #define MCS_GAMMA_DELTA_X_RED 0xb6
32 #define MCS_GAMMA_DELTA_Y_GREEN 0xb7
33 #define MCS_GAMMA_DELTA_X_GREEN 0xb8
34 #define MCS_GAMMA_DELTA_Y_BLUE 0xb9
35 #define MCS_GAMMA_DELTA_X_BLUE 0xba
36 #define MCS_MIECTL1 0xc0
37 #define MCS_BCMODE 0xc1
38 #define MCS_ERROR_CHECK 0xd5
39 #define MCS_READ_ID1 0xda
40 #define MCS_READ_ID2 0xdb
41 #define MCS_READ_ID3 0xdc
42 #define MCS_LEVEL_2_KEY 0xf0
43 #define MCS_MTP_KEY 0xf1
44 #define MCS_DISCTL 0xf2
45 #define MCS_SRCCTL 0xf6
46 #define MCS_IFCTL 0xf7
47 #define MCS_PANELCTL 0xf8
48 #define MCS_PGAMMACTL 0xfa
50 #define S6E63M0_LCD_ID_VALUE_M2 0xA4
51 #define S6E63M0_LCD_ID_VALUE_SM2 0xB4
52 #define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6
54 #define NUM_GAMMA_LEVELS 28
55 #define GAMMA_TABLE_COUNT 23
57 #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
59 /* array of gamma tables for gamma value 2.2 */
60 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
62 { MCS_PGAMMACTL, 0x02,
63 0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE,
64 0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD,
65 0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, },
67 { MCS_PGAMMACTL, 0x02,
68 0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC,
69 0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC,
70 0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, },
72 { MCS_PGAMMACTL, 0x02,
73 0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB,
74 0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9,
75 0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, },
77 { MCS_PGAMMACTL, 0x02,
78 0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9,
79 0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7,
80 0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, },
82 { MCS_PGAMMACTL, 0x02,
83 0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7,
84 0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7,
85 0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, },
87 { MCS_PGAMMACTL, 0x02,
88 0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9,
89 0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5,
90 0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, },
92 { MCS_PGAMMACTL, 0x02,
93 0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7,
94 0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5,
95 0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, },
97 { MCS_PGAMMACTL, 0x02,
98 0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6,
99 0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4,
100 0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, },
102 { MCS_PGAMMACTL, 0x02,
103 0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5,
104 0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4,
105 0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, },
107 { MCS_PGAMMACTL, 0x02,
108 0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4,
109 0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2,
110 0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, },
112 { MCS_PGAMMACTL, 0x02,
113 0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3,
114 0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2,
115 0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, },
117 { MCS_PGAMMACTL, 0x02,
118 0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1,
119 0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0,
120 0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, },
122 { MCS_PGAMMACTL, 0x02,
123 0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1,
124 0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0,
125 0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, },
127 { MCS_PGAMMACTL, 0x02,
128 0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0,
129 0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF,
130 0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, },
132 { MCS_PGAMMACTL, 0x02,
133 0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0,
134 0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF,
135 0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, },
137 { MCS_PGAMMACTL, 0x02,
138 0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0,
139 0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE,
140 0xBF, 0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, },
142 { MCS_PGAMMACTL, 0x02,
143 0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1,
144 0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE,
145 0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, },
147 { MCS_PGAMMACTL, 0x02,
148 0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1,
149 0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD,
150 0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, },
152 { MCS_PGAMMACTL, 0x02,
153 0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0,
154 0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC,
155 0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, },
157 { MCS_PGAMMACTL, 0x02,
158 0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF,
159 0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC,
160 0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, },
162 { MCS_PGAMMACTL, 0x02,
163 0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF,
164 0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB,
165 0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, },
167 { MCS_PGAMMACTL, 0x02,
168 0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0,
169 0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB,
170 0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, },
172 { MCS_PGAMMACTL, 0x02,
173 0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF,
174 0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA,
175 0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, },
177 { MCS_PGAMMACTL, 0x02,
178 0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD,
179 0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9,
180 0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, },
182 { MCS_PGAMMACTL, 0x02,
183 0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD,
184 0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9,
185 0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, },
187 { MCS_PGAMMACTL, 0x02,
188 0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE,
189 0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8,
190 0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, },
192 { MCS_PGAMMACTL, 0x02,
193 0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD,
194 0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7,
195 0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, },
197 { MCS_PGAMMACTL, 0x02,
198 0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC,
199 0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7,
200 0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, },
203 #define NUM_ACL_LEVELS 7
204 #define ACL_TABLE_COUNT 28
206 static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
209 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
210 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
215 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
216 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26,
221 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
222 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29,
227 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
228 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B,
233 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
234 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E,
239 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
240 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F,
245 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
246 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
247 0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31,
251 /* This tells us which ACL level goes with which gamma */
252 static u8 const s6e63m0_acl_per_gamma[NUM_GAMMA_LEVELS] = {
253 /* 30 - 60 cd: ACL off/NULL */
255 /* 70 - 250 cd: 40P ACL */
256 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
257 /* 260 - 300 cd: 50P ACL */
261 /* The ELVSS backlight regulator has 5 levels */
262 #define S6E63M0_ELVSS_LEVELS 5
264 static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
266 0x0D, /* 30 cd - 100 cd */
267 0x09, /* 110 cd - 160 cd */
268 0x07, /* 170 cd - 200 cd */
269 0x00, /* 210 cd - 300 cd */
272 /* This tells us which ELVSS level goes with which gamma */
273 static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
275 1, 1, 1, 1, 1, 1, 1, 1,
281 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
286 int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val);
287 int (*dcs_write)(struct device *dev, const u8 *data, size_t len);
288 struct drm_panel panel;
289 struct backlight_device *bl_dev;
294 struct regulator_bulk_data supplies[2];
295 struct gpio_desc *reset_gpio;
301 * This field is tested by functions directly accessing bus before
302 * transfer, transfer is skipped if it is set. In case of transfer
303 * failure or unexpected response the field is set to error value.
304 * Such construct allows to eliminate many checks in higher level
310 static const struct drm_display_mode default_mode = {
313 .hsync_start = 480 + 16,
314 .hsync_end = 480 + 16 + 2,
315 .htotal = 480 + 16 + 2 + 16,
317 .vsync_start = 800 + 28,
318 .vsync_end = 800 + 28 + 2,
319 .vtotal = 800 + 28 + 2 + 1,
322 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
325 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
327 return container_of(panel, struct s6e63m0, panel);
330 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
332 int ret = ctx->error;
338 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
343 ctx->error = ctx->dcs_read(ctx->dev, cmd, data);
346 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
348 if (ctx->error < 0 || len == 0)
351 ctx->error = ctx->dcs_write(ctx->dev, data, len);
354 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
356 static const u8 d[] = { seq }; \
357 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
360 static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
365 s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
366 s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
367 s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
369 ret = s6e63m0_clear_error(ctx);
371 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
372 ctx->lcd_type = 0x00;
376 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
379 * We attempt to detect what panel is mounted on the controller.
380 * The third ID byte represents the desired ELVSS pulse for
384 case S6E63M0_LCD_ID_VALUE_M2:
385 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
386 ctx->elvss_pulse = id3;
388 case S6E63M0_LCD_ID_VALUE_SM2:
389 case S6E63M0_LCD_ID_VALUE_SM2_1:
390 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
391 ctx->elvss_pulse = id3;
394 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
395 /* Default ELVSS pulse level */
396 ctx->elvss_pulse = 0x16;
405 static void s6e63m0_init(struct s6e63m0 *ctx)
408 * We do not know why there is a difference in the DSI mode.
411 * In the vendor driver this sequence is called
412 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
415 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
416 0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3,
417 0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00);
419 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
420 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
421 0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
423 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
424 0x02, 0x03, 0x1c, 0x10, 0x10);
425 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
428 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
429 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
430 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
431 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
433 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
436 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
438 s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
440 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED,
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_RED,
448 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
449 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
452 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN,
453 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
454 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
455 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
456 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
457 0x21, 0x20, 0x1e, 0x1e);
459 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN,
460 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
461 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
464 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE,
465 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
466 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
467 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
468 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
469 0x21, 0x20, 0x1e, 0x1e);
471 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE,
472 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
473 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
476 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
477 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
478 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
480 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
482 s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
483 0x10, 0x10, 0x0b, 0x05);
485 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
488 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
492 static int s6e63m0_power_on(struct s6e63m0 *ctx)
496 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
502 /* Be sure to send a reset pulse */
503 gpiod_set_value(ctx->reset_gpio, 1);
505 gpiod_set_value(ctx->reset_gpio, 0);
511 static int s6e63m0_power_off(struct s6e63m0 *ctx)
515 gpiod_set_value(ctx->reset_gpio, 1);
518 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
525 static int s6e63m0_disable(struct drm_panel *panel)
527 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
532 backlight_disable(ctx->bl_dev);
534 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
536 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
539 ctx->enabled = false;
544 static int s6e63m0_unprepare(struct drm_panel *panel)
546 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
552 s6e63m0_clear_error(ctx);
554 ret = s6e63m0_power_off(ctx);
558 ctx->prepared = false;
563 static int s6e63m0_prepare(struct drm_panel *panel)
565 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
571 ret = s6e63m0_power_on(ctx);
575 /* Magic to unlock level 2 control of the display */
576 s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
577 /* Magic to unlock MTP reading */
578 s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
580 ret = s6e63m0_check_lcd_type(ctx);
586 ret = s6e63m0_clear_error(ctx);
589 s6e63m0_unprepare(panel);
591 ctx->prepared = true;
596 static int s6e63m0_enable(struct drm_panel *panel)
598 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
603 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
605 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
608 s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
609 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
610 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
613 backlight_enable(ctx->bl_dev);
620 static int s6e63m0_get_modes(struct drm_panel *panel,
621 struct drm_connector *connector)
623 struct drm_display_mode *mode;
624 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
626 mode = drm_mode_duplicate(connector->dev, &default_mode);
628 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
629 default_mode.hdisplay, default_mode.vdisplay,
630 drm_mode_vrefresh(&default_mode));
634 connector->display_info.width_mm = mode->width_mm;
635 connector->display_info.height_mm = mode->height_mm;
636 drm_display_info_set_bus_formats(&connector->display_info,
638 connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
639 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
641 drm_mode_set_name(mode);
643 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
644 drm_mode_probed_add(connector, mode);
649 static const struct drm_panel_funcs s6e63m0_drm_funcs = {
650 .disable = s6e63m0_disable,
651 .unprepare = s6e63m0_unprepare,
652 .prepare = s6e63m0_prepare,
653 .enable = s6e63m0_enable,
654 .get_modes = s6e63m0_get_modes,
657 static int s6e63m0_set_brightness(struct backlight_device *bd)
659 struct s6e63m0 *ctx = bl_get_data(bd);
660 int brightness = bd->props.brightness;
665 /* Adjust ELVSS to candela level */
666 i = s6e63m0_elvss_per_gamma[brightness];
667 elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];
668 if (elvss_val > 0x1f)
670 elvss_cmd_set[0] = MCS_TEMP_SWIRE;
671 elvss_cmd_set[1] = elvss_val;
672 elvss_cmd_set[2] = elvss_val;
673 elvss_cmd_set[3] = elvss_val;
674 elvss_cmd_set[4] = elvss_val;
675 s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);
677 /* Update the ACL per gamma value */
678 i = s6e63m0_acl_per_gamma[brightness];
679 s6e63m0_dcs_write(ctx, s6e63m0_acl[i],
680 ARRAY_SIZE(s6e63m0_acl[i]));
682 /* Update gamma table */
683 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
684 ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
685 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);
688 return s6e63m0_clear_error(ctx);
691 static const struct backlight_ops s6e63m0_backlight_ops = {
692 .update_status = s6e63m0_set_brightness,
695 static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
697 struct backlight_properties props = {
698 .type = BACKLIGHT_RAW,
699 .brightness = max_brightness,
700 .max_brightness = max_brightness,
702 struct device *dev = ctx->dev;
705 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
706 &s6e63m0_backlight_ops,
708 if (IS_ERR(ctx->bl_dev)) {
709 ret = PTR_ERR(ctx->bl_dev);
710 dev_err(dev, "error registering backlight device (%d)\n", ret);
716 int s6e63m0_probe(struct device *dev,
717 int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val),
718 int (*dcs_write)(struct device *dev, const u8 *data, size_t len),
725 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
729 ctx->dsi_mode = dsi_mode;
730 ctx->dcs_read = dcs_read;
731 ctx->dcs_write = dcs_write;
732 dev_set_drvdata(dev, ctx);
735 ctx->enabled = false;
736 ctx->prepared = false;
738 ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
740 max_brightness = MAX_BRIGHTNESS;
741 if (max_brightness > MAX_BRIGHTNESS) {
742 dev_err(dev, "illegal max brightness specified\n");
743 max_brightness = MAX_BRIGHTNESS;
746 ctx->supplies[0].supply = "vdd3";
747 ctx->supplies[1].supply = "vci";
748 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
751 dev_err(dev, "failed to get regulators: %d\n", ret);
755 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
756 if (IS_ERR(ctx->reset_gpio)) {
757 dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
758 return PTR_ERR(ctx->reset_gpio);
761 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
762 dsi_mode ? DRM_MODE_CONNECTOR_DSI :
763 DRM_MODE_CONNECTOR_DPI);
765 ret = s6e63m0_backlight_register(ctx, max_brightness);
769 drm_panel_add(&ctx->panel);
773 EXPORT_SYMBOL_GPL(s6e63m0_probe);
775 int s6e63m0_remove(struct device *dev)
777 struct s6e63m0 *ctx = dev_get_drvdata(dev);
779 drm_panel_remove(&ctx->panel);
783 EXPORT_SYMBOL_GPL(s6e63m0_remove);
786 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
787 MODULE_LICENSE("GPL v2");