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/regulator/consumer.h>
19 #include <linux/media-bus-format.h>
20
21 #include <video/mipi_display.h>
22
23 #include "panel-samsung-s6e63m0.h"
24
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
49
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
53
54 #define NUM_GAMMA_LEVELS 28
55 #define GAMMA_TABLE_COUNT 23
56
57 #define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
58
59 /* array of gamma tables for gamma value 2.2 */
60 static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
61 /* 30 cd */
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, },
66 /* 40 cd */
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, },
71 /* 50 cd */
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, },
76 /* 60 cd */
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, },
81 /* 70 cd */
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, },
86 /* 80 cd */
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, },
91 /* 90 cd */
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, },
96 /* 100 cd */
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, },
101 /* 110 cd */
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, },
106 /* 120 cd */
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, },
111 /* 130 cd */
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, },
116 /* 140 cd */
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, },
121 /* 150 cd */
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, },
126 /* 160 cd */
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, },
131 /* 170 cd */
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, },
136 /* 180 cd */
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, },
141 /* 190 cd */
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, },
146 /* 200 cd */
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, },
151 /* 210 cd */
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, },
156 /* 220 cd */
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, },
161 /* 230 cd */
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, },
166 /* 240 cd */
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, },
171 /* 250 cd */
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, },
176 /* 260 cd */
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, },
181 /* 270 cd */
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, },
186 /* 280 cd */
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, },
191 /* 290 cd */
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, },
196 /* 300 cd */
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, },
201 };
202
203 #define NUM_ACL_LEVELS 7
204 #define ACL_TABLE_COUNT 28
205
206 static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
207 /* NULL ACL */
208 { MCS_BCMODE,
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,
212 0x00, 0x00, 0x00 },
213 /* 40P ACL */
214 { MCS_BCMODE,
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,
218 0x2B, 0x31, 0x36 },
219 /* 43P ACL */
220 { MCS_BCMODE,
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,
224 0x2F, 0x34, 0x3A },
225 /* 45P ACL */
226 { MCS_BCMODE,
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,
230 0x31, 0x37, 0x3D },
231 /* 47P ACL */
232 { MCS_BCMODE,
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,
236 0x34, 0x3B, 0x41 },
237 /* 48P ACL */
238 { MCS_BCMODE,
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,
242 0x36, 0x3C, 0x43 },
243 /* 50P ACL */
244 { MCS_BCMODE,
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,
248 0x38, 0x3F, 0x46 },
249 };
250
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 */
254 0, 0, 0, 0,
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 */
258 6, 6, 6, 6, 6,
259 };
260
261 /* The ELVSS backlight regulator has 5 levels */
262 #define S6E63M0_ELVSS_LEVELS 5
263
264 static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
265 0x00, /* not set */
266 0x0D, /* 30 cd - 100 cd */
267 0x09, /* 110 cd - 160 cd */
268 0x07, /* 170 cd - 200 cd */
269 0x00, /* 210 cd - 300 cd */
270 };
271
272 /* This tells us which ELVSS level goes with which gamma */
273 static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
274 /* 30 - 100 cd */
275 1, 1, 1, 1, 1, 1, 1, 1,
276 /* 110 - 160 cd */
277 2, 2, 2, 2, 2, 2,
278 /* 170 - 200 cd */
279 3, 3, 3, 3,
280 /* 210 - 300 cd */
281 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
282 };
283
284 struct s6e63m0 {
285 struct device *dev;
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;
290 u8 lcd_type;
291 u8 elvss_pulse;
292 bool dsi_mode;
293
294 struct regulator_bulk_data supplies[2];
295 struct gpio_desc *reset_gpio;
296
297 bool prepared;
298 bool enabled;
299
300 /*
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
305 * functions.
306 */
307 int error;
308 };
309
310 static const struct drm_display_mode default_mode = {
311 .clock = 25628,
312 .hdisplay = 480,
313 .hsync_start = 480 + 16,
314 .hsync_end = 480 + 16 + 2,
315 .htotal = 480 + 16 + 2 + 16,
316 .vdisplay = 800,
317 .vsync_start = 800 + 28,
318 .vsync_end = 800 + 28 + 2,
319 .vtotal = 800 + 28 + 2 + 1,
320 .width_mm = 53,
321 .height_mm = 89,
322 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
323 };
324
panel_to_s6e63m0(struct drm_panel * panel)325 static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
326 {
327 return container_of(panel, struct s6e63m0, panel);
328 }
329
s6e63m0_clear_error(struct s6e63m0 * ctx)330 static int s6e63m0_clear_error(struct s6e63m0 *ctx)
331 {
332 int ret = ctx->error;
333
334 ctx->error = 0;
335 return ret;
336 }
337
s6e63m0_dcs_read(struct s6e63m0 * ctx,const u8 cmd,u8 * data)338 static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
339 {
340 if (ctx->error < 0)
341 return;
342
343 ctx->error = ctx->dcs_read(ctx->dev, cmd, data);
344 }
345
s6e63m0_dcs_write(struct s6e63m0 * ctx,const u8 * data,size_t len)346 static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
347 {
348 if (ctx->error < 0 || len == 0)
349 return;
350
351 ctx->error = ctx->dcs_write(ctx->dev, data, len);
352 }
353
354 #define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
355 ({ \
356 static const u8 d[] = { seq }; \
357 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
358 })
359
s6e63m0_check_lcd_type(struct s6e63m0 * ctx)360 static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
361 {
362 u8 id1, id2, id3;
363 int ret;
364
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);
368
369 ret = s6e63m0_clear_error(ctx);
370 if (ret) {
371 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
372 ctx->lcd_type = 0x00;
373 return ret;
374 }
375
376 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
377
378 /*
379 * We attempt to detect what panel is mounted on the controller.
380 * The third ID byte represents the desired ELVSS pulse for
381 * some displays.
382 */
383 switch (id2) {
384 case S6E63M0_LCD_ID_VALUE_M2:
385 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
386 ctx->elvss_pulse = id3;
387 break;
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;
392 break;
393 default:
394 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
395 /* Default ELVSS pulse level */
396 ctx->elvss_pulse = 0x16;
397 break;
398 }
399
400 ctx->lcd_type = id2;
401
402 return 0;
403 }
404
s6e63m0_init(struct s6e63m0 * ctx)405 static void s6e63m0_init(struct s6e63m0 *ctx)
406 {
407 /*
408 * We do not know why there is a difference in the DSI mode.
409 * (No datasheet.)
410 *
411 * In the vendor driver this sequence is called
412 * "SEQ_PANEL_CONDITION_SET" or "DCS_CMD_SEQ_PANEL_COND_SET".
413 */
414 if (ctx->dsi_mode)
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);
418 else
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);
422
423 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
424 0x02, 0x03, 0x1c, 0x10, 0x10);
425 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
426 0x03, 0x00, 0x00);
427
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,
432 0xd6);
433 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
434 0x01);
435
436 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
437 0x00, 0x8e, 0x07);
438 s6e63m0_dcs_write_seq_static(ctx, MCS_PENTILE_1, 0x6c);
439
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);
446
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,
450 0x66, 0x66);
451
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);
458
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,
462 0x66, 0x66);
463
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);
470
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,
474 0x66, 0x66);
475
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);
481
482 s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
483 0x10, 0x10, 0x0b, 0x05);
484
485 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
486 0x01);
487
488 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
489 0x0b);
490 }
491
s6e63m0_power_on(struct s6e63m0 * ctx)492 static int s6e63m0_power_on(struct s6e63m0 *ctx)
493 {
494 int ret;
495
496 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
497 if (ret < 0)
498 return ret;
499
500 msleep(25);
501
502 /* Be sure to send a reset pulse */
503 gpiod_set_value(ctx->reset_gpio, 1);
504 msleep(5);
505 gpiod_set_value(ctx->reset_gpio, 0);
506 msleep(120);
507
508 return 0;
509 }
510
s6e63m0_power_off(struct s6e63m0 * ctx)511 static int s6e63m0_power_off(struct s6e63m0 *ctx)
512 {
513 int ret;
514
515 gpiod_set_value(ctx->reset_gpio, 1);
516 msleep(120);
517
518 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
519 if (ret < 0)
520 return ret;
521
522 return 0;
523 }
524
s6e63m0_disable(struct drm_panel * panel)525 static int s6e63m0_disable(struct drm_panel *panel)
526 {
527 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
528
529 if (!ctx->enabled)
530 return 0;
531
532 backlight_disable(ctx->bl_dev);
533
534 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
535 msleep(10);
536 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
537 msleep(120);
538
539 ctx->enabled = false;
540
541 return 0;
542 }
543
s6e63m0_unprepare(struct drm_panel * panel)544 static int s6e63m0_unprepare(struct drm_panel *panel)
545 {
546 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
547 int ret;
548
549 if (!ctx->prepared)
550 return 0;
551
552 s6e63m0_clear_error(ctx);
553
554 ret = s6e63m0_power_off(ctx);
555 if (ret < 0)
556 return ret;
557
558 ctx->prepared = false;
559
560 return 0;
561 }
562
s6e63m0_prepare(struct drm_panel * panel)563 static int s6e63m0_prepare(struct drm_panel *panel)
564 {
565 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
566 int ret;
567
568 if (ctx->prepared)
569 return 0;
570
571 ret = s6e63m0_power_on(ctx);
572 if (ret < 0)
573 return ret;
574
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);
579
580 ret = s6e63m0_check_lcd_type(ctx);
581 if (ret < 0)
582 return ret;
583
584 s6e63m0_init(ctx);
585
586 ret = s6e63m0_clear_error(ctx);
587
588 if (ret < 0)
589 s6e63m0_unprepare(panel);
590
591 ctx->prepared = true;
592
593 return ret;
594 }
595
s6e63m0_enable(struct drm_panel * panel)596 static int s6e63m0_enable(struct drm_panel *panel)
597 {
598 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
599
600 if (ctx->enabled)
601 return 0;
602
603 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
604 msleep(120);
605 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
606 msleep(10);
607
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,
611 0x0F, 0x00);
612
613 backlight_enable(ctx->bl_dev);
614
615 ctx->enabled = true;
616
617 return 0;
618 }
619
s6e63m0_get_modes(struct drm_panel * panel,struct drm_connector * connector)620 static int s6e63m0_get_modes(struct drm_panel *panel,
621 struct drm_connector *connector)
622 {
623 struct drm_display_mode *mode;
624 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
625
626 mode = drm_mode_duplicate(connector->dev, &default_mode);
627 if (!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));
631 return -ENOMEM;
632 }
633
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,
637 &bus_format, 1);
638 connector->display_info.bus_flags = DRM_BUS_FLAG_DE_LOW |
639 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
640
641 drm_mode_set_name(mode);
642
643 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
644 drm_mode_probed_add(connector, mode);
645
646 return 1;
647 }
648
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,
655 };
656
s6e63m0_set_brightness(struct backlight_device * bd)657 static int s6e63m0_set_brightness(struct backlight_device *bd)
658 {
659 struct s6e63m0 *ctx = bl_get_data(bd);
660 int brightness = bd->props.brightness;
661 u8 elvss_val;
662 u8 elvss_cmd_set[5];
663 int i;
664
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)
669 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);
676
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]));
681
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);
686
687
688 return s6e63m0_clear_error(ctx);
689 }
690
691 static const struct backlight_ops s6e63m0_backlight_ops = {
692 .update_status = s6e63m0_set_brightness,
693 };
694
s6e63m0_backlight_register(struct s6e63m0 * ctx,u32 max_brightness)695 static int s6e63m0_backlight_register(struct s6e63m0 *ctx, u32 max_brightness)
696 {
697 struct backlight_properties props = {
698 .type = BACKLIGHT_RAW,
699 .brightness = max_brightness,
700 .max_brightness = max_brightness,
701 };
702 struct device *dev = ctx->dev;
703 int ret = 0;
704
705 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
706 &s6e63m0_backlight_ops,
707 &props);
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);
711 }
712
713 return ret;
714 }
715
s6e63m0_probe(struct device * dev,int (* dcs_read)(struct device * dev,const u8 cmd,u8 * val),int (* dcs_write)(struct device * dev,const u8 * data,size_t len),bool dsi_mode)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),
719 bool dsi_mode)
720 {
721 struct s6e63m0 *ctx;
722 u32 max_brightness;
723 int ret;
724
725 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
726 if (!ctx)
727 return -ENOMEM;
728
729 ctx->dsi_mode = dsi_mode;
730 ctx->dcs_read = dcs_read;
731 ctx->dcs_write = dcs_write;
732 dev_set_drvdata(dev, ctx);
733
734 ctx->dev = dev;
735 ctx->enabled = false;
736 ctx->prepared = false;
737
738 ret = device_property_read_u32(dev, "max-brightness", &max_brightness);
739 if (ret)
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;
744 }
745
746 ctx->supplies[0].supply = "vdd3";
747 ctx->supplies[1].supply = "vci";
748 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
749 ctx->supplies);
750 if (ret < 0) {
751 dev_err(dev, "failed to get regulators: %d\n", ret);
752 return ret;
753 }
754
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);
759 }
760
761 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
762 dsi_mode ? DRM_MODE_CONNECTOR_DSI :
763 DRM_MODE_CONNECTOR_DPI);
764
765 ret = s6e63m0_backlight_register(ctx, max_brightness);
766 if (ret < 0)
767 return ret;
768
769 drm_panel_add(&ctx->panel);
770
771 return 0;
772 }
773 EXPORT_SYMBOL_GPL(s6e63m0_probe);
774
s6e63m0_remove(struct device * dev)775 int s6e63m0_remove(struct device *dev)
776 {
777 struct s6e63m0 *ctx = dev_get_drvdata(dev);
778
779 drm_panel_remove(&ctx->panel);
780
781 return 0;
782 }
783 EXPORT_SYMBOL_GPL(s6e63m0_remove);
784
785 MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
786 MODULE_DESCRIPTION("s6e63m0 LCD Driver");
787 MODULE_LICENSE("GPL v2");
788