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 /* Manufacturer Command Set */
26 #define MCS_ELVSS_ON 0xb1
27 #define MCS_TEMP_SWIRE 0xb2
28 #define MCS_MIECTL1 0xc0
29 #define MCS_BCMODE 0xc1
30 #define MCS_ERROR_CHECK 0xd5
31 #define MCS_READ_ID1 0xda
32 #define MCS_READ_ID2 0xdb
33 #define MCS_READ_ID3 0xdc
34 #define MCS_LEVEL_2_KEY 0xf0
35 #define MCS_MTP_KEY 0xf1
36 #define MCS_DISCTL 0xf2
37 #define MCS_SRCCTL 0xf6
38 #define MCS_IFCTL 0xf7
39 #define MCS_PANELCTL 0xf8
40 #define MCS_PGAMMACTL 0xfa
42 #define S6E63M0_LCD_ID_VALUE_M2 0xA4
43 #define S6E63M0_LCD_ID_VALUE_SM2 0xB4
44 #define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6
46 #define NUM_GAMMA_LEVELS 28
47 #define GAMMA_TABLE_COUNT 23
49 #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
51 /* array of gamma tables for gamma value 2.2 */
52 static u8
const s6e63m0_gamma_22
[NUM_GAMMA_LEVELS
][GAMMA_TABLE_COUNT
] = {
54 { MCS_PGAMMACTL
, 0x02,
55 0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE,
56 0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD,
57 0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, },
59 { MCS_PGAMMACTL
, 0x02,
60 0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC,
61 0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC,
62 0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, },
64 { MCS_PGAMMACTL
, 0x02,
65 0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB,
66 0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9,
67 0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, },
69 { MCS_PGAMMACTL
, 0x02,
70 0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9,
71 0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7,
72 0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, },
74 { MCS_PGAMMACTL
, 0x02,
75 0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7,
76 0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7,
77 0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, },
79 { MCS_PGAMMACTL
, 0x02,
80 0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9,
81 0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5,
82 0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, },
84 { MCS_PGAMMACTL
, 0x02,
85 0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7,
86 0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5,
87 0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, },
89 { MCS_PGAMMACTL
, 0x02,
90 0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6,
91 0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4,
92 0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, },
94 { MCS_PGAMMACTL
, 0x02,
95 0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5,
96 0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4,
97 0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, },
99 { MCS_PGAMMACTL
, 0x02,
100 0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4,
101 0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2,
102 0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, },
104 { MCS_PGAMMACTL
, 0x02,
105 0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3,
106 0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2,
107 0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, },
109 { MCS_PGAMMACTL
, 0x02,
110 0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1,
111 0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0,
112 0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, },
114 { MCS_PGAMMACTL
, 0x02,
115 0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1,
116 0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0,
117 0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, },
119 { MCS_PGAMMACTL
, 0x02,
120 0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0,
121 0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF,
122 0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, },
124 { MCS_PGAMMACTL
, 0x02,
125 0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0,
126 0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF,
127 0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, },
129 { MCS_PGAMMACTL
, 0x02,
130 0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0,
131 0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE,
132 0xBF, 0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, },
134 { MCS_PGAMMACTL
, 0x02,
135 0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1,
136 0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE,
137 0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, },
139 { MCS_PGAMMACTL
, 0x02,
140 0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1,
141 0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD,
142 0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, },
144 { MCS_PGAMMACTL
, 0x02,
145 0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0,
146 0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC,
147 0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, },
149 { MCS_PGAMMACTL
, 0x02,
150 0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF,
151 0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC,
152 0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, },
154 { MCS_PGAMMACTL
, 0x02,
155 0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF,
156 0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB,
157 0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, },
159 { MCS_PGAMMACTL
, 0x02,
160 0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0,
161 0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB,
162 0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, },
164 { MCS_PGAMMACTL
, 0x02,
165 0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF,
166 0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA,
167 0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, },
169 { MCS_PGAMMACTL
, 0x02,
170 0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD,
171 0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9,
172 0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, },
174 { MCS_PGAMMACTL
, 0x02,
175 0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD,
176 0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9,
177 0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, },
179 { MCS_PGAMMACTL
, 0x02,
180 0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE,
181 0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8,
182 0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, },
184 { MCS_PGAMMACTL
, 0x02,
185 0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD,
186 0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7,
187 0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, },
189 { MCS_PGAMMACTL
, 0x02,
190 0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC,
191 0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7,
192 0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, },
195 #define NUM_ACL_LEVELS 7
196 #define ACL_TABLE_COUNT 28
198 static u8
const s6e63m0_acl
[NUM_ACL_LEVELS
][ACL_TABLE_COUNT
] = {
201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
203 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
207 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
208 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
209 0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26,
213 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
214 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
215 0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29,
219 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
220 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
221 0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B,
225 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
226 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E,
231 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
232 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F,
237 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
238 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31,
243 /* This tells us which ACL level goes with which gamma */
244 static u8
const s6e63m0_acl_per_gamma
[NUM_GAMMA_LEVELS
] = {
245 /* 30 - 60 cd: ACL off/NULL */
247 /* 70 - 250 cd: 40P ACL */
248 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
249 /* 260 - 300 cd: 50P ACL */
253 /* The ELVSS backlight regulator has 5 levels */
254 #define S6E63M0_ELVSS_LEVELS 5
256 static u8
const s6e63m0_elvss_offsets
[S6E63M0_ELVSS_LEVELS
] = {
258 0x0D, /* 30 cd - 100 cd */
259 0x09, /* 110 cd - 160 cd */
260 0x07, /* 170 cd - 200 cd */
261 0x00, /* 210 cd - 300 cd */
264 /* This tells us which ELVSS level goes with which gamma */
265 static u8
const s6e63m0_elvss_per_gamma
[NUM_GAMMA_LEVELS
] = {
267 1, 1, 1, 1, 1, 1, 1, 1,
273 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
278 int (*dcs_read
)(struct device
*dev
, const u8 cmd
, u8
*val
);
279 int (*dcs_write
)(struct device
*dev
, const u8
*data
, size_t len
);
280 struct drm_panel panel
;
281 struct backlight_device
*bl_dev
;
285 struct regulator_bulk_data supplies
[2];
286 struct gpio_desc
*reset_gpio
;
292 * This field is tested by functions directly accessing bus before
293 * transfer, transfer is skipped if it is set. In case of transfer
294 * failure or unexpected response the field is set to error value.
295 * Such construct allows to eliminate many checks in higher level
301 static const struct drm_display_mode default_mode
= {
304 .hsync_start
= 480 + 16,
305 .hsync_end
= 480 + 16 + 2,
306 .htotal
= 480 + 16 + 2 + 16,
308 .vsync_start
= 800 + 28,
309 .vsync_end
= 800 + 28 + 2,
310 .vtotal
= 800 + 28 + 2 + 1,
313 .flags
= DRM_MODE_FLAG_NVSYNC
| DRM_MODE_FLAG_NHSYNC
,
316 static inline struct s6e63m0
*panel_to_s6e63m0(struct drm_panel
*panel
)
318 return container_of(panel
, struct s6e63m0
, panel
);
321 static int s6e63m0_clear_error(struct s6e63m0
*ctx
)
323 int ret
= ctx
->error
;
329 static void s6e63m0_dcs_read(struct s6e63m0
*ctx
, const u8 cmd
, u8
*data
)
334 ctx
->error
= ctx
->dcs_read(ctx
->dev
, cmd
, data
);
337 static void s6e63m0_dcs_write(struct s6e63m0
*ctx
, const u8
*data
, size_t len
)
339 if (ctx
->error
< 0 || len
== 0)
342 ctx
->error
= ctx
->dcs_write(ctx
->dev
, data
, len
);
345 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
347 static const u8 d[] = { seq }; \
348 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
351 static int s6e63m0_check_lcd_type(struct s6e63m0
*ctx
)
356 s6e63m0_dcs_read(ctx
, MCS_READ_ID1
, &id1
);
357 s6e63m0_dcs_read(ctx
, MCS_READ_ID2
, &id2
);
358 s6e63m0_dcs_read(ctx
, MCS_READ_ID3
, &id3
);
360 ret
= s6e63m0_clear_error(ctx
);
362 dev_err(ctx
->dev
, "error checking LCD type (%d)\n", ret
);
363 ctx
->lcd_type
= 0x00;
367 dev_info(ctx
->dev
, "MTP ID: %02x %02x %02x\n", id1
, id2
, id3
);
370 * We attempt to detect what panel is mounted on the controller.
371 * The third ID byte represents the desired ELVSS pulse for
375 case S6E63M0_LCD_ID_VALUE_M2
:
376 dev_info(ctx
->dev
, "detected LCD panel AMS397GE MIPI M2\n");
377 ctx
->elvss_pulse
= id3
;
379 case S6E63M0_LCD_ID_VALUE_SM2
:
380 case S6E63M0_LCD_ID_VALUE_SM2_1
:
381 dev_info(ctx
->dev
, "detected LCD panel AMS397GE MIPI SM2\n");
382 ctx
->elvss_pulse
= id3
;
385 dev_info(ctx
->dev
, "unknown LCD panel type %02x\n", id2
);
386 /* Default ELVSS pulse level */
387 ctx
->elvss_pulse
= 0x16;
396 static void s6e63m0_init(struct s6e63m0
*ctx
)
398 s6e63m0_dcs_write_seq_static(ctx
, MCS_PANELCTL
,
399 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
400 0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
402 s6e63m0_dcs_write_seq_static(ctx
, MCS_DISCTL
,
403 0x02, 0x03, 0x1c, 0x10, 0x10);
404 s6e63m0_dcs_write_seq_static(ctx
, MCS_IFCTL
,
407 s6e63m0_dcs_write_seq_static(ctx
, MCS_PGAMMACTL
,
408 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
409 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
410 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
412 s6e63m0_dcs_write_seq_static(ctx
, MCS_PGAMMACTL
,
415 s6e63m0_dcs_write_seq_static(ctx
, MCS_SRCCTL
,
417 s6e63m0_dcs_write_seq_static(ctx
, 0xb3, 0x6c);
419 s6e63m0_dcs_write_seq_static(ctx
, 0xb5,
420 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
421 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
422 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
423 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
424 0x21, 0x20, 0x1e, 0x1e);
426 s6e63m0_dcs_write_seq_static(ctx
, 0xb6,
427 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
428 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
431 s6e63m0_dcs_write_seq_static(ctx
, 0xb7,
432 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
433 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
434 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
435 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
436 0x21, 0x20, 0x1e, 0x1e);
438 s6e63m0_dcs_write_seq_static(ctx
, 0xb8,
439 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
440 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
443 s6e63m0_dcs_write_seq_static(ctx
, 0xb9,
444 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
445 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
446 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
447 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
448 0x21, 0x20, 0x1e, 0x1e);
450 s6e63m0_dcs_write_seq_static(ctx
, 0xba,
451 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
452 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
455 s6e63m0_dcs_write_seq_static(ctx
, MCS_BCMODE
,
456 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
457 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
458 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
459 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
461 s6e63m0_dcs_write_seq_static(ctx
, MCS_TEMP_SWIRE
,
462 0x10, 0x10, 0x0b, 0x05);
464 s6e63m0_dcs_write_seq_static(ctx
, MCS_MIECTL1
,
467 s6e63m0_dcs_write_seq_static(ctx
, MCS_ELVSS_ON
,
471 static int s6e63m0_power_on(struct s6e63m0
*ctx
)
475 ret
= regulator_bulk_enable(ARRAY_SIZE(ctx
->supplies
), ctx
->supplies
);
481 /* Be sure to send a reset pulse */
482 gpiod_set_value(ctx
->reset_gpio
, 1);
484 gpiod_set_value(ctx
->reset_gpio
, 0);
490 static int s6e63m0_power_off(struct s6e63m0
*ctx
)
494 gpiod_set_value(ctx
->reset_gpio
, 1);
497 ret
= regulator_bulk_disable(ARRAY_SIZE(ctx
->supplies
), ctx
->supplies
);
504 static int s6e63m0_disable(struct drm_panel
*panel
)
506 struct s6e63m0
*ctx
= panel_to_s6e63m0(panel
);
511 backlight_disable(ctx
->bl_dev
);
513 s6e63m0_dcs_write_seq_static(ctx
, MIPI_DCS_SET_DISPLAY_OFF
);
515 s6e63m0_dcs_write_seq_static(ctx
, MIPI_DCS_ENTER_SLEEP_MODE
);
518 ctx
->enabled
= false;
523 static int s6e63m0_unprepare(struct drm_panel
*panel
)
525 struct s6e63m0
*ctx
= panel_to_s6e63m0(panel
);
531 s6e63m0_clear_error(ctx
);
533 ret
= s6e63m0_power_off(ctx
);
537 ctx
->prepared
= false;
542 static int s6e63m0_prepare(struct drm_panel
*panel
)
544 struct s6e63m0
*ctx
= panel_to_s6e63m0(panel
);
550 ret
= s6e63m0_power_on(ctx
);
554 /* Magic to unlock level 2 control of the display */
555 s6e63m0_dcs_write_seq_static(ctx
, MCS_LEVEL_2_KEY
, 0x5a, 0x5a);
556 /* Magic to unlock MTP reading */
557 s6e63m0_dcs_write_seq_static(ctx
, MCS_MTP_KEY
, 0x5a, 0x5a);
559 ret
= s6e63m0_check_lcd_type(ctx
);
565 ret
= s6e63m0_clear_error(ctx
);
568 s6e63m0_unprepare(panel
);
570 ctx
->prepared
= true;
575 static int s6e63m0_enable(struct drm_panel
*panel
)
577 struct s6e63m0
*ctx
= panel_to_s6e63m0(panel
);
582 s6e63m0_dcs_write_seq_static(ctx
, MIPI_DCS_EXIT_SLEEP_MODE
);
584 s6e63m0_dcs_write_seq_static(ctx
, MIPI_DCS_SET_DISPLAY_ON
);
587 s6e63m0_dcs_write_seq_static(ctx
, MCS_ERROR_CHECK
,
588 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
589 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
592 backlight_enable(ctx
->bl_dev
);
599 static int s6e63m0_get_modes(struct drm_panel
*panel
,
600 struct drm_connector
*connector
)
602 struct drm_display_mode
*mode
;
603 static const u32 bus_format
= MEDIA_BUS_FMT_RGB888_1X24
;
605 mode
= drm_mode_duplicate(connector
->dev
, &default_mode
);
607 dev_err(panel
->dev
, "failed to add mode %ux%u@%u\n",
608 default_mode
.hdisplay
, default_mode
.vdisplay
,
609 drm_mode_vrefresh(&default_mode
));
613 connector
->display_info
.width_mm
= mode
->width_mm
;
614 connector
->display_info
.height_mm
= mode
->height_mm
;
615 drm_display_info_set_bus_formats(&connector
->display_info
,
617 connector
->display_info
.bus_flags
= DRM_BUS_FLAG_DE_LOW
|
618 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE
;
620 drm_mode_set_name(mode
);
622 mode
->type
= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
623 drm_mode_probed_add(connector
, mode
);
628 static const struct drm_panel_funcs s6e63m0_drm_funcs
= {
629 .disable
= s6e63m0_disable
,
630 .unprepare
= s6e63m0_unprepare
,
631 .prepare
= s6e63m0_prepare
,
632 .enable
= s6e63m0_enable
,
633 .get_modes
= s6e63m0_get_modes
,
636 static int s6e63m0_set_brightness(struct backlight_device
*bd
)
638 struct s6e63m0
*ctx
= bl_get_data(bd
);
639 int brightness
= bd
->props
.brightness
;
644 /* Adjust ELVSS to candela level */
645 i
= s6e63m0_elvss_per_gamma
[brightness
];
646 elvss_val
= ctx
->elvss_pulse
+ s6e63m0_elvss_offsets
[i
];
647 if (elvss_val
> 0x1f)
649 elvss_cmd_set
[0] = MCS_TEMP_SWIRE
;
650 elvss_cmd_set
[1] = elvss_val
;
651 elvss_cmd_set
[2] = elvss_val
;
652 elvss_cmd_set
[3] = elvss_val
;
653 elvss_cmd_set
[4] = elvss_val
;
654 s6e63m0_dcs_write(ctx
, elvss_cmd_set
, 5);
656 /* Update the ACL per gamma value */
657 i
= s6e63m0_acl_per_gamma
[brightness
];
658 s6e63m0_dcs_write(ctx
, s6e63m0_acl
[i
],
659 ARRAY_SIZE(s6e63m0_acl
[i
]));
661 /* Update gamma table */
662 s6e63m0_dcs_write(ctx
, s6e63m0_gamma_22
[brightness
],
663 ARRAY_SIZE(s6e63m0_gamma_22
[brightness
]));
664 s6e63m0_dcs_write_seq_static(ctx
, MCS_PGAMMACTL
, 0x03);
667 return s6e63m0_clear_error(ctx
);
670 static const struct backlight_ops s6e63m0_backlight_ops
= {
671 .update_status
= s6e63m0_set_brightness
,
674 static int s6e63m0_backlight_register(struct s6e63m0
*ctx
)
676 struct backlight_properties props
= {
677 .type
= BACKLIGHT_RAW
,
678 .brightness
= MAX_BRIGHTNESS
,
679 .max_brightness
= MAX_BRIGHTNESS
681 struct device
*dev
= ctx
->dev
;
684 ctx
->bl_dev
= devm_backlight_device_register(dev
, "panel", dev
, ctx
,
685 &s6e63m0_backlight_ops
,
687 if (IS_ERR(ctx
->bl_dev
)) {
688 ret
= PTR_ERR(ctx
->bl_dev
);
689 dev_err(dev
, "error registering backlight device (%d)\n", ret
);
695 int s6e63m0_probe(struct device
*dev
,
696 int (*dcs_read
)(struct device
*dev
, const u8 cmd
, u8
*val
),
697 int (*dcs_write
)(struct device
*dev
, const u8
*data
, size_t len
),
703 ctx
= devm_kzalloc(dev
, sizeof(struct s6e63m0
), GFP_KERNEL
);
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 ctx
->supplies
[0].supply
= "vdd3";
716 ctx
->supplies
[1].supply
= "vci";
717 ret
= devm_regulator_bulk_get(dev
, ARRAY_SIZE(ctx
->supplies
),
720 dev_err(dev
, "failed to get regulators: %d\n", ret
);
724 ctx
->reset_gpio
= devm_gpiod_get(dev
, "reset", GPIOD_OUT_HIGH
);
725 if (IS_ERR(ctx
->reset_gpio
)) {
726 dev_err(dev
, "cannot get reset-gpios %ld\n", PTR_ERR(ctx
->reset_gpio
));
727 return PTR_ERR(ctx
->reset_gpio
);
730 drm_panel_init(&ctx
->panel
, dev
, &s6e63m0_drm_funcs
,
731 dsi_mode
? DRM_MODE_CONNECTOR_DSI
:
732 DRM_MODE_CONNECTOR_DPI
);
734 ret
= s6e63m0_backlight_register(ctx
);
738 drm_panel_add(&ctx
->panel
);
742 EXPORT_SYMBOL_GPL(s6e63m0_probe
);
744 int s6e63m0_remove(struct device
*dev
)
746 struct s6e63m0
*ctx
= dev_get_drvdata(dev
);
748 drm_panel_remove(&ctx
->panel
);
752 EXPORT_SYMBOL_GPL(s6e63m0_remove
);
754 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
755 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
756 MODULE_LICENSE("GPL v2");