WIP FPC-III support
[linux/fpc-iii.git] / drivers / gpu / drm / panel / panel-samsung-s6e63m0.c
blob6b4e97bfd46ee01e3c0b8db6d338663af37ecc7e
1 // SPDX-License-Identifier: GPL-2.0
2 /*
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>
9 */
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] = {
53 /* 30 cd */
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, },
58 /* 40 cd */
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, },
63 /* 50 cd */
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, },
68 /* 60 cd */
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, },
73 /* 70 cd */
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, },
78 /* 80 cd */
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, },
83 /* 90 cd */
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, },
88 /* 100 cd */
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, },
93 /* 110 cd */
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, },
98 /* 120 cd */
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, },
103 /* 130 cd */
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, },
108 /* 140 cd */
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, },
113 /* 150 cd */
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, },
118 /* 160 cd */
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, },
123 /* 170 cd */
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, },
128 /* 180 cd */
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, },
133 /* 190 cd */
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, },
138 /* 200 cd */
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, },
143 /* 210 cd */
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, },
148 /* 220 cd */
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, },
153 /* 230 cd */
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, },
158 /* 240 cd */
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, },
163 /* 250 cd */
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, },
168 /* 260 cd */
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, },
173 /* 270 cd */
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, },
178 /* 280 cd */
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, },
183 /* 290 cd */
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, },
188 /* 300 cd */
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] = {
199 /* NULL ACL */
200 { MCS_BCMODE,
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,
204 0x00, 0x00, 0x00 },
205 /* 40P ACL */
206 { MCS_BCMODE,
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,
210 0x2B, 0x31, 0x36 },
211 /* 43P ACL */
212 { MCS_BCMODE,
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,
216 0x2F, 0x34, 0x3A },
217 /* 45P ACL */
218 { MCS_BCMODE,
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,
222 0x31, 0x37, 0x3D },
223 /* 47P ACL */
224 { MCS_BCMODE,
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,
228 0x34, 0x3B, 0x41 },
229 /* 48P ACL */
230 { MCS_BCMODE,
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,
234 0x36, 0x3C, 0x43 },
235 /* 50P ACL */
236 { MCS_BCMODE,
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,
240 0x38, 0x3F, 0x46 },
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 */
246 0, 0, 0, 0,
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 */
250 6, 6, 6, 6, 6,
253 /* The ELVSS backlight regulator has 5 levels */
254 #define S6E63M0_ELVSS_LEVELS 5
256 static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
257 0x00, /* not set */
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] = {
266 /* 30 - 100 cd */
267 1, 1, 1, 1, 1, 1, 1, 1,
268 /* 110 - 160 cd */
269 2, 2, 2, 2, 2, 2,
270 /* 170 - 200 cd */
271 3, 3, 3, 3,
272 /* 210 - 300 cd */
273 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
276 struct s6e63m0 {
277 struct device *dev;
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;
282 u8 lcd_type;
283 u8 elvss_pulse;
285 struct regulator_bulk_data supplies[2];
286 struct gpio_desc *reset_gpio;
288 bool prepared;
289 bool enabled;
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
296 * functions.
298 int error;
301 static const struct drm_display_mode default_mode = {
302 .clock = 25628,
303 .hdisplay = 480,
304 .hsync_start = 480 + 16,
305 .hsync_end = 480 + 16 + 2,
306 .htotal = 480 + 16 + 2 + 16,
307 .vdisplay = 800,
308 .vsync_start = 800 + 28,
309 .vsync_end = 800 + 28 + 2,
310 .vtotal = 800 + 28 + 2 + 1,
311 .width_mm = 53,
312 .height_mm = 89,
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;
325 ctx->error = 0;
326 return ret;
329 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
331 if (ctx->error < 0)
332 return;
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)
340 return;
342 ctx->error = ctx->dcs_write(ctx->dev, data, len);
345 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
346 ({ \
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)
353 u8 id1, id2, id3;
354 int ret;
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);
361 if (ret) {
362 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
363 ctx->lcd_type = 0x00;
364 return ret;
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
372 * some displays.
374 switch (id2) {
375 case S6E63M0_LCD_ID_VALUE_M2:
376 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
377 ctx->elvss_pulse = id3;
378 break;
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;
383 break;
384 default:
385 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
386 /* Default ELVSS pulse level */
387 ctx->elvss_pulse = 0x16;
388 break;
391 ctx->lcd_type = id2;
393 return 0;
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,
405 0x03, 0x00, 0x00);
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,
411 0xd6);
412 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
413 0x01);
415 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
416 0x00, 0x8e, 0x07);
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,
429 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,
441 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,
453 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,
465 0x01);
467 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
468 0x0b);
471 static int s6e63m0_power_on(struct s6e63m0 *ctx)
473 int ret;
475 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
476 if (ret < 0)
477 return ret;
479 msleep(25);
481 /* Be sure to send a reset pulse */
482 gpiod_set_value(ctx->reset_gpio, 1);
483 msleep(5);
484 gpiod_set_value(ctx->reset_gpio, 0);
485 msleep(120);
487 return 0;
490 static int s6e63m0_power_off(struct s6e63m0 *ctx)
492 int ret;
494 gpiod_set_value(ctx->reset_gpio, 1);
495 msleep(120);
497 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
498 if (ret < 0)
499 return ret;
501 return 0;
504 static int s6e63m0_disable(struct drm_panel *panel)
506 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
508 if (!ctx->enabled)
509 return 0;
511 backlight_disable(ctx->bl_dev);
513 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
514 msleep(10);
515 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
516 msleep(120);
518 ctx->enabled = false;
520 return 0;
523 static int s6e63m0_unprepare(struct drm_panel *panel)
525 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
526 int ret;
528 if (!ctx->prepared)
529 return 0;
531 s6e63m0_clear_error(ctx);
533 ret = s6e63m0_power_off(ctx);
534 if (ret < 0)
535 return ret;
537 ctx->prepared = false;
539 return 0;
542 static int s6e63m0_prepare(struct drm_panel *panel)
544 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
545 int ret;
547 if (ctx->prepared)
548 return 0;
550 ret = s6e63m0_power_on(ctx);
551 if (ret < 0)
552 return ret;
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);
560 if (ret < 0)
561 return ret;
563 s6e63m0_init(ctx);
565 ret = s6e63m0_clear_error(ctx);
567 if (ret < 0)
568 s6e63m0_unprepare(panel);
570 ctx->prepared = true;
572 return ret;
575 static int s6e63m0_enable(struct drm_panel *panel)
577 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
579 if (ctx->enabled)
580 return 0;
582 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
583 msleep(120);
584 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
585 msleep(10);
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,
590 0x0F, 0x00);
592 backlight_enable(ctx->bl_dev);
594 ctx->enabled = true;
596 return 0;
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);
606 if (!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));
610 return -ENOMEM;
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,
616 &bus_format, 1);
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);
625 return 1;
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;
640 u8 elvss_val;
641 u8 elvss_cmd_set[5];
642 int i;
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)
648 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;
682 int ret = 0;
684 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
685 &s6e63m0_backlight_ops,
686 &props);
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);
692 return 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),
698 bool dsi_mode)
700 struct s6e63m0 *ctx;
701 int ret;
703 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
704 if (!ctx)
705 return -ENOMEM;
707 ctx->dcs_read = dcs_read;
708 ctx->dcs_write = dcs_write;
709 dev_set_drvdata(dev, ctx);
711 ctx->dev = dev;
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),
718 ctx->supplies);
719 if (ret < 0) {
720 dev_err(dev, "failed to get regulators: %d\n", ret);
721 return 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);
735 if (ret < 0)
736 return ret;
738 drm_panel_add(&ctx->panel);
740 return 0;
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);
750 return 0;
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");