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/export.h>
17 #include <linux/gpio/consumer.h>
18 #include <linux/module.h>
19 #include <linux/property.h>
20 #include <linux/regulator/consumer.h>
21 #include <linux/media-bus-format.h>
22
23 #include <video/mipi_display.h>
24
25 #include "panel-samsung-s6e63m0.h"
26
27 #define S6E63M0_LCD_ID_VALUE_M2 0xA4
28 #define S6E63M0_LCD_ID_VALUE_SM2 0xB4
29 #define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6
30
31 #define NUM_GAMMA_LEVELS 28
32 #define GAMMA_TABLE_COUNT 23
33
34 #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
35
36 /* array of gamma tables for gamma value 2.2 */
37 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
38 /* 30 cd */
39 { MCS_PGAMMACTL, 0x02,
40 0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE,
41 0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD,
42 0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, },
43 /* 40 cd */
44 { MCS_PGAMMACTL, 0x02,
45 0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC,
46 0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC,
47 0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, },
48 /* 50 cd */
49 { MCS_PGAMMACTL, 0x02,
50 0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB,
51 0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9,
52 0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, },
53 /* 60 cd */
54 { MCS_PGAMMACTL, 0x02,
55 0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9,
56 0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7,
57 0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, },
58 /* 70 cd */
59 { MCS_PGAMMACTL, 0x02,
60 0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7,
61 0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7,
62 0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, },
63 /* 80 cd */
64 { MCS_PGAMMACTL, 0x02,
65 0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9,
66 0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5,
67 0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, },
68 /* 90 cd */
69 { MCS_PGAMMACTL, 0x02,
70 0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7,
71 0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5,
72 0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, },
73 /* 100 cd */
74 { MCS_PGAMMACTL, 0x02,
75 0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6,
76 0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4,
77 0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, },
78 /* 110 cd */
79 { MCS_PGAMMACTL, 0x02,
80 0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5,
81 0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4,
82 0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, },
83 /* 120 cd */
84 { MCS_PGAMMACTL, 0x02,
85 0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4,
86 0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2,
87 0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, },
88 /* 130 cd */
89 { MCS_PGAMMACTL, 0x02,
90 0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3,
91 0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2,
92 0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, },
93 /* 140 cd */
94 { MCS_PGAMMACTL, 0x02,
95 0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1,
96 0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0,
97 0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, },
98 /* 150 cd */
99 { MCS_PGAMMACTL, 0x02,
100 0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1,
101 0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0,
102 0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, },
103 /* 160 cd */
104 { MCS_PGAMMACTL, 0x02,
105 0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0,
106 0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF,
107 0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, },
108 /* 170 cd */
109 { MCS_PGAMMACTL, 0x02,
110 0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0,
111 0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF,
112 0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, },
113 /* 180 cd */
114 { MCS_PGAMMACTL, 0x02,
115 0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0,
116 0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE,
117 0xBF, 0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, },
118 /* 190 cd */
119 { MCS_PGAMMACTL, 0x02,
120 0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1,
121 0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE,
122 0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, },
123 /* 200 cd */
124 { MCS_PGAMMACTL, 0x02,
125 0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1,
126 0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD,
127 0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, },
128 /* 210 cd */
129 { MCS_PGAMMACTL, 0x02,
130 0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0,
131 0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC,
132 0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, },
133 /* 220 cd */
134 { MCS_PGAMMACTL, 0x02,
135 0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF,
136 0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC,
137 0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, },
138 /* 230 cd */
139 { MCS_PGAMMACTL, 0x02,
140 0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF,
141 0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB,
142 0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, },
143 /* 240 cd */
144 { MCS_PGAMMACTL, 0x02,
145 0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0,
146 0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB,
147 0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, },
148 /* 250 cd */
149 { MCS_PGAMMACTL, 0x02,
150 0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF,
151 0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA,
152 0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, },
153 /* 260 cd */
154 { MCS_PGAMMACTL, 0x02,
155 0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD,
156 0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9,
157 0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, },
158 /* 270 cd */
159 { MCS_PGAMMACTL, 0x02,
160 0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD,
161 0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9,
162 0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, },
163 /* 280 cd */
164 { MCS_PGAMMACTL, 0x02,
165 0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE,
166 0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8,
167 0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, },
168 /* 290 cd */
169 { MCS_PGAMMACTL, 0x02,
170 0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD,
171 0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7,
172 0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, },
173 /* 300 cd */
174 { MCS_PGAMMACTL, 0x02,
175 0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC,
176 0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7,
177 0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, },
178 };
179
180 #define NUM_ACL_LEVELS 7
181 #define ACL_TABLE_COUNT 28
182
183 static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
184 /* NULL ACL */
185 { MCS_BCMODE,
186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
188 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00 },
190 /* 40P ACL */
191 { MCS_BCMODE,
192 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
193 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26,
195 0x2B, 0x31, 0x36 },
196 /* 43P ACL */
197 { MCS_BCMODE,
198 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
199 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
200 0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29,
201 0x2F, 0x34, 0x3A },
202 /* 45P ACL */
203 { MCS_BCMODE,
204 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
205 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
206 0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B,
207 0x31, 0x37, 0x3D },
208 /* 47P ACL */
209 { MCS_BCMODE,
210 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
211 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
212 0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E,
213 0x34, 0x3B, 0x41 },
214 /* 48P ACL */
215 { MCS_BCMODE,
216 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
217 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F,
219 0x36, 0x3C, 0x43 },
220 /* 50P ACL */
221 { MCS_BCMODE,
222 0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
223 0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31,
225 0x38, 0x3F, 0x46 },
226 };
227
228 /* This tells us which ACL level goes with which gamma */
229 static u8 const s6e63m0_acl_per_gamma[NUM_GAMMA_LEVELS] = {
230 /* 30 - 60 cd: ACL off/NULL */
231 0, 0, 0, 0,
232 /* 70 - 250 cd: 40P ACL */
233 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
234 /* 260 - 300 cd: 50P ACL */
235 6, 6, 6, 6, 6,
236 };
237
238 /* The ELVSS backlight regulator has 5 levels */
239 #define S6E63M0_ELVSS_LEVELS 5
240
241 static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
242 0x00, /* not set */
243 0x0D, /* 30 cd - 100 cd */
244 0x09, /* 110 cd - 160 cd */
245 0x07, /* 170 cd - 200 cd */
246 0x00, /* 210 cd - 300 cd */
247 };
248
249 /* This tells us which ELVSS level goes with which gamma */
250 static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
251 /* 30 - 100 cd */
252 1, 1, 1, 1, 1, 1, 1, 1,
253 /* 110 - 160 cd */
254 2, 2, 2, 2, 2, 2,
255 /* 170 - 200 cd */
256 3, 3, 3, 3,
257 /* 210 - 300 cd */
258 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
259 };
260
261 struct s6e63m0 {
262 struct device *dev;
263 void *transport_data;
264 int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val);
265 int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len);
266 struct drm_panel panel;
267 struct backlight_device *bl_dev;
268 u8 lcd_type;
269 u8 elvss_pulse;
270 bool dsi_mode;
271
272 struct regulator_bulk_data supplies[2];
273 struct gpio_desc *reset_gpio;
274
275 /*
276 * This field is tested by functions directly accessing bus before
277 * transfer, transfer is skipped if it is set. In case of transfer
278 * failure or unexpected response the field is set to error value.
279 * Such construct allows to eliminate many checks in higher level
280 * functions.
281 */
282 int error;
283 };
284
285 static const struct drm_display_mode default_mode = {
286 .clock = 25628,
287 .hdisplay = 480,
288 .hsync_start = 480 + 16,
289 .hsync_end = 480 + 16 + 2,
290 .htotal = 480 + 16 + 2 + 16,
291 .vdisplay = 800,
292 .vsync_start = 800 + 28,
293 .vsync_end = 800 + 28 + 2,
294 .vtotal = 800 + 28 + 2 + 1,
295 .width_mm = 53,
296 .height_mm = 89,
297 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
298 };
299
panel_to_s6e63m0(struct drm_panel * panel)300 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
301 {
302 return container_of(panel, struct s6e63m0, panel);
303 }
304
s6e63m0_clear_error(struct s6e63m0 * ctx)305 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
306 {
307 int ret = ctx->error;
308
309 ctx->error = 0;
310 return ret;
311 }
312
s6e63m0_dcs_read(struct s6e63m0 * ctx,const u8 cmd,u8 * data)313 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
314 {
315 if (ctx->error < 0)
316 return;
317
318 ctx->error = ctx->dcs_read(ctx->dev, ctx->transport_data, cmd, data);
319 }
320
s6e63m0_dcs_write(struct s6e63m0 * ctx,const u8 * data,size_t len)321 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
322 {
323 if (ctx->error < 0 || len == 0)
324 return;
325
326 ctx->error = ctx->dcs_write(ctx->dev, ctx->transport_data, data, len);
327 }
328
329 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
330 ({ \
331 static const u8 d[] = { seq }; \
332 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
333 })
334
s6e63m0_check_lcd_type(struct s6e63m0 * ctx)335 static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
336 {
337 u8 id1, id2, id3;
338 int ret;
339
340 s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
341 s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
342 s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
343
344 ret = s6e63m0_clear_error(ctx);
345 if (ret) {
346 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
347 ctx->lcd_type = 0x00;
348 return ret;
349 }
350
351 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
352
353 /*
354 * We attempt to detect what panel is mounted on the controller.
355 * The third ID byte represents the desired ELVSS pulse for
356 * some displays.
357 */
358 switch (id2) {
359 case S6E63M0_LCD_ID_VALUE_M2:
360 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
361 ctx->elvss_pulse = id3;
362 break;
363 case S6E63M0_LCD_ID_VALUE_SM2:
364 case S6E63M0_LCD_ID_VALUE_SM2_1:
365 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
366 ctx->elvss_pulse = id3;
367 break;
368 default:
369 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
370 /* Default ELVSS pulse level */
371 ctx->elvss_pulse = 0x16;
372 break;
373 }
374
375 ctx->lcd_type = id2;
376
377 return 0;
378 }
379
s6e63m0_init(struct s6e63m0 * ctx)380 static void s6e63m0_init(struct s6e63m0 *ctx)
381 {
382 /*
383 * We do not know why there is a difference in the DSI mode.
384 * (No datasheet.)
385 *
386 * In the vendor driver this sequence is called
387 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
388 */
389 if (ctx->dsi_mode)
390 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
391 0x01, 0x2c, 0x2c, 0x07, 0x07, 0x5f, 0xb3,
392 0x6d, 0x97, 0x1d, 0x3a, 0x0f, 0x00, 0x00);
393 else
394 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
395 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
396 0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
397
398 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
399 0x02, 0x03, 0x1c, 0x10, 0x10);
400 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
401 0x03, 0x00, 0x00);
402
403 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
404 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
405 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
406 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
407 0xd6);
408 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
409 0x01);
410
411 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
412 0x00, 0x8e, 0x07);
413 s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
414
415 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_RED,
416 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
417 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
418 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
419 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
420 0x21, 0x20, 0x1e, 0x1e);
421
422 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_RED,
423 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
424 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
425 0x66, 0x66);
426
427 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_GREEN,
428 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
429 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
430 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
431 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
432 0x21, 0x20, 0x1e, 0x1e);
433
434 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_GREEN,
435 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
436 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
437 0x66, 0x66);
438
439 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_Y_BLUE,
440 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
441 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
442 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
443 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
444 0x21, 0x20, 0x1e, 0x1e);
445
446 s6e63m0_dcs_write_seq_static(ctx, MCS_GAMMA_DELTA_X_BLUE,
447 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
448 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
449 0x66, 0x66);
450
451 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
452 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
453 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
454 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
455 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
456
457 s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
458 0x10, 0x10, 0x0b, 0x05);
459
460 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
461 0x01);
462
463 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
464 0x0b);
465 }
466
s6e63m0_power_on(struct s6e63m0 * ctx)467 static int s6e63m0_power_on(struct s6e63m0 *ctx)
468 {
469 int ret;
470
471 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
472 if (ret < 0)
473 return ret;
474
475 msleep(25);
476
477 /* Be sure to send a reset pulse */
478 gpiod_set_value(ctx->reset_gpio, 1);
479 msleep(5);
480 gpiod_set_value(ctx->reset_gpio, 0);
481 msleep(120);
482
483 return 0;
484 }
485
s6e63m0_power_off(struct s6e63m0 * ctx)486 static int s6e63m0_power_off(struct s6e63m0 *ctx)
487 {
488 int ret;
489
490 gpiod_set_value(ctx->reset_gpio, 1);
491 msleep(120);
492
493 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
494 if (ret < 0)
495 return ret;
496
497 return 0;
498 }
499
s6e63m0_disable(struct drm_panel * panel)500 static int s6e63m0_disable(struct drm_panel *panel)
501 {
502 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
503
504 backlight_disable(ctx->bl_dev);
505
506 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
507 msleep(10);
508 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
509 msleep(120);
510
511 return 0;
512 }
513
s6e63m0_unprepare(struct drm_panel * panel)514 static int s6e63m0_unprepare(struct drm_panel *panel)
515 {
516 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
517 int ret;
518
519 s6e63m0_clear_error(ctx);
520
521 ret = s6e63m0_power_off(ctx);
522 if (ret < 0)
523 return ret;
524
525 return 0;
526 }
527
s6e63m0_prepare(struct drm_panel * panel)528 static int s6e63m0_prepare(struct drm_panel *panel)
529 {
530 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
531 int ret;
532
533 ret = s6e63m0_power_on(ctx);
534 if (ret < 0)
535 return ret;
536
537 /* Magic to unlock level 2 control of the display */
538 s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
539 /* Magic to unlock MTP reading */
540 s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
541
542 ret = s6e63m0_check_lcd_type(ctx);
543 if (ret < 0)
544 return ret;
545
546 s6e63m0_init(ctx);
547
548 ret = s6e63m0_clear_error(ctx);
549
550 if (ret < 0)
551 s6e63m0_unprepare(panel);
552
553 return ret;
554 }
555
s6e63m0_enable(struct drm_panel * panel)556 static int s6e63m0_enable(struct drm_panel *panel)
557 {
558 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
559
560 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
561 msleep(120);
562 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
563 msleep(10);
564
565 s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
566 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
567 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
568 0x0F, 0x00);
569
570 backlight_enable(ctx->bl_dev);
571
572 return 0;
573 }
574
s6e63m0_get_modes(struct drm_panel * panel,struct drm_connector * connector)575 static int s6e63m0_get_modes(struct drm_panel *panel,
576 struct drm_connector *connector)
577 {
578 struct drm_display_mode *mode;
579 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
580
581 mode = drm_mode_duplicate(connector->dev, &default_mode);
582 if (!mode) {
583 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
584 default_mode.hdisplay, default_mode.vdisplay,
585 drm_mode_vrefresh(&default_mode));
586 return -ENOMEM;
587 }
588
589 connector->display_info.width_mm = mode->width_mm;
590 connector->display_info.height_mm = mode->height_mm;
591 drm_display_info_set_bus_formats(&connector->display_info,
592 &bus_format, 1);
593 connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
594 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
595
596 drm_mode_set_name(mode);
597
598 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
599 drm_mode_probed_add(connector, mode);
600
601 return 1;
602 }
603
604 static const struct drm_panel_funcs s6e63m0_drm_funcs = {
605 .disable = s6e63m0_disable,
606 .unprepare = s6e63m0_unprepare,
607 .prepare = s6e63m0_prepare,
608 .enable = s6e63m0_enable,
609 .get_modes = s6e63m0_get_modes,
610 };
611
s6e63m0_set_brightness(struct backlight_device * bd)612 static int s6e63m0_set_brightness(struct backlight_device *bd)
613 {
614 struct s6e63m0 *ctx = bl_get_data(bd);
615 int brightness = bd->props.brightness;
616 u8 elvss_val;
617 u8 elvss_cmd_set[5];
618 int i;
619
620 /* Adjust ELVSS to candela level */
621 i = s6e63m0_elvss_per_gamma[brightness];
622 elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];
623 if (elvss_val > 0x1f)
624 elvss_val = 0x1f;
625 elvss_cmd_set[0] = MCS_TEMP_SWIRE;
626 elvss_cmd_set[1] = elvss_val;
627 elvss_cmd_set[2] = elvss_val;
628 elvss_cmd_set[3] = elvss_val;
629 elvss_cmd_set[4] = elvss_val;
630 s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);
631
632 /* Update the ACL per gamma value */
633 i = s6e63m0_acl_per_gamma[brightness];
634 s6e63m0_dcs_write(ctx, s6e63m0_acl[i],
635 ARRAY_SIZE(s6e63m0_acl[i]));
636
637 /* Update gamma table */
638 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
639 ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
640 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);
641
642
643 return s6e63m0_clear_error(ctx);
644 }
645
646 static const struct backlight_ops s6e63m0_backlight_ops = {
647 .update_status = s6e63m0_set_brightness,
648 };
649
s6e63m0_backlight_register(struct s6e63m0 * ctx,u32 max_brightness)650 static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
651 {
652 struct backlight_properties props = {
653 .type = BACKLIGHT_RAW,
654 .brightness = max_brightness,
655 .max_brightness = max_brightness,
656 };
657 struct device *dev = ctx->dev;
658 int ret = 0;
659
660 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
661 &s6e63m0_backlight_ops,
662 &props);
663 if (IS_ERR(ctx->bl_dev)) {
664 ret = PTR_ERR(ctx->bl_dev);
665 dev_err(dev, "error registering backlight device (%d)\n", ret);
666 }
667
668 return ret;
669 }
670
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)671 int s6e63m0_probe(struct device *dev, void *trsp,
672 int (*dcs_read)(struct device *dev, void *trsp, const u8 cmd, u8 *val),
673 int (*dcs_write)(struct device *dev, void *trsp, const u8 *data, size_t len),
674 bool dsi_mode)
675 {
676 struct s6e63m0 *ctx;
677 u32 max_brightness;
678 int ret;
679
680 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
681 if (!ctx)
682 return -ENOMEM;
683
684 ctx->transport_data = trsp;
685 ctx->dsi_mode = dsi_mode;
686 ctx->dcs_read = dcs_read;
687 ctx->dcs_write = dcs_write;
688 dev_set_drvdata(dev, ctx);
689
690 ctx->dev = dev;
691
692 ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
693 if (ret)
694 max_brightness = MAX_BRIGHTNESS;
695 if (max_brightness > MAX_BRIGHTNESS) {
696 dev_err(dev, "illegal max brightness specified\n");
697 max_brightness = MAX_BRIGHTNESS;
698 }
699
700 ctx->supplies[0].supply = "vdd3";
701 ctx->supplies[1].supply = "vci";
702 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
703 ctx->supplies);
704 if (ret < 0) {
705 dev_err(dev, "failed to get regulators: %d\n", ret);
706 return ret;
707 }
708
709 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
710 if (IS_ERR(ctx->reset_gpio)) {
711 dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
712 return PTR_ERR(ctx->reset_gpio);
713 }
714
715 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
716 dsi_mode ? DRM_MODE_CONNECTOR_DSI :
717 DRM_MODE_CONNECTOR_DPI);
718
719 ret = s6e63m0_backlight_register(ctx, max_brightness);
720 if (ret < 0)
721 return ret;
722
723 drm_panel_add(&ctx->panel);
724
725 return 0;
726 }
727 EXPORT_SYMBOL_GPL(s6e63m0_probe);
728
s6e63m0_remove(struct device * dev)729 void s6e63m0_remove(struct device *dev)
730 {
731 struct s6e63m0 *ctx = dev_get_drvdata(dev);
732
733 drm_panel_remove(&ctx->panel);
734 }
735 EXPORT_SYMBOL_GPL(s6e63m0_remove);
736
737 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
738 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
739 MODULE_LICENSE("GPL v2");
740