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