Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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_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
41
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
45
46#define NUM_GAMMA_LEVELS 28
47#define GAMMA_TABLE_COUNT 23
48
49#define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
50
51/* array of gamma tables for gamma value 2.2 */
52static 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, },
193};
194
195#define NUM_ACL_LEVELS 7
196#define ACL_TABLE_COUNT 28
197
198static 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 },
241};
242
243/* This tells us which ACL level goes with which gamma */
244static 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,
251};
252
253/* The ELVSS backlight regulator has 5 levels */
254#define S6E63M0_ELVSS_LEVELS 5
255
256static 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 */
262};
263
264/* This tells us which ELVSS level goes with which gamma */
265static 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,
274};
275
276struct 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;
284
285 struct regulator_bulk_data supplies[2];
286 struct gpio_desc *reset_gpio;
287
288 bool prepared;
289 bool enabled;
290
291 /*
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.
297 */
298 int error;
299};
300
301static 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,
314};
315
316static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
317{
318 return container_of(panel, struct s6e63m0, panel);
319}
320
321static int s6e63m0_clear_error(struct s6e63m0 *ctx)
322{
323 int ret = ctx->error;
324
325 ctx->error = 0;
326 return ret;
327}
328
329static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
330{
331 if (ctx->error < 0)
332 return;
333
334 ctx->error = ctx->dcs_read(ctx->dev, cmd, data);
335}
336
337static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
338{
339 if (ctx->error < 0 || len == 0)
340 return;
341
342 ctx->error = ctx->dcs_write(ctx->dev, data, len);
343}
344
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)); \
349 })
350
351static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
352{
353 u8 id1, id2, id3;
354 int ret;
355
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);
359
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;
365 }
366
367 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
368
369 /*
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.
373 */
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;
389 }
390
391 ctx->lcd_type = id2;
392
393 return 0;
394}
395
396static void s6e63m0_init(struct s6e63m0 *ctx)
397{
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);
401
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);
406
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);
414
415 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
416 0x00, 0x8e, 0x07);
417 s6e63m0_dcs_write_seq_static(ctx, 0xb3, 0x6c);
418
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);
425
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);
430
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);
437
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);
442
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);
449
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);
454
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);
460
461 s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
462 0x10, 0x10, 0x0b, 0x05);
463
464 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
465 0x01);
466
467 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
468 0x0b);
469}
470
471static int s6e63m0_power_on(struct s6e63m0 *ctx)
472{
473 int ret;
474
475 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
476 if (ret < 0)
477 return ret;
478
479 msleep(25);
480
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);
486
487 return 0;
488}
489
490static int s6e63m0_power_off(struct s6e63m0 *ctx)
491{
492 int ret;
493
494 gpiod_set_value(ctx->reset_gpio, 1);
495 msleep(120);
496
497 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
498 if (ret < 0)
499 return ret;
500
501 return 0;
502}
503
504static int s6e63m0_disable(struct drm_panel *panel)
505{
506 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
507
508 if (!ctx->enabled)
509 return 0;
510
511 backlight_disable(ctx->bl_dev);
512
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);
517
518 ctx->enabled = false;
519
520 return 0;
521}
522
523static int s6e63m0_unprepare(struct drm_panel *panel)
524{
525 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
526 int ret;
527
528 if (!ctx->prepared)
529 return 0;
530
531 s6e63m0_clear_error(ctx);
532
533 ret = s6e63m0_power_off(ctx);
534 if (ret < 0)
535 return ret;
536
537 ctx->prepared = false;
538
539 return 0;
540}
541
542static int s6e63m0_prepare(struct drm_panel *panel)
543{
544 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
545 int ret;
546
547 if (ctx->prepared)
548 return 0;
549
550 ret = s6e63m0_power_on(ctx);
551 if (ret < 0)
552 return ret;
553
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);
558
559 ret = s6e63m0_check_lcd_type(ctx);
560 if (ret < 0)
561 return ret;
562
563 s6e63m0_init(ctx);
564
565 ret = s6e63m0_clear_error(ctx);
566
567 if (ret < 0)
568 s6e63m0_unprepare(panel);
569
570 ctx->prepared = true;
571
572 return ret;
573}
574
575static int s6e63m0_enable(struct drm_panel *panel)
576{
577 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
578
579 if (ctx->enabled)
580 return 0;
581
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);
586
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);
591
592 backlight_enable(ctx->bl_dev);
593
594 ctx->enabled = true;
595
596 return 0;
597}
598
599static int s6e63m0_get_modes(struct drm_panel *panel,
600 struct drm_connector *connector)
601{
602 struct drm_display_mode *mode;
603 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
604
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;
611 }
612
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;
619
620 drm_mode_set_name(mode);
621
622 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
623 drm_mode_probed_add(connector, mode);
624
625 return 1;
626}
627
628static 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,
634};
635
636static int s6e63m0_set_brightness(struct backlight_device *bd)
637{
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;
643
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);
655
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]));
660
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);
665
666
667 return s6e63m0_clear_error(ctx);
668}
669
670static const struct backlight_ops s6e63m0_backlight_ops = {
671 .update_status = s6e63m0_set_brightness,
672};
673
674static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
675{
676 struct backlight_properties props = {
677 .type = BACKLIGHT_RAW,
678 .brightness = MAX_BRIGHTNESS,
679 .max_brightness = MAX_BRIGHTNESS
680 };
681 struct device *dev = ctx->dev;
682 int ret = 0;
683
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);
690 }
691
692 return ret;
693}
694
695int 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)
699{
700 struct s6e63m0 *ctx;
701 int ret;
702
703 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
704 if (!ctx)
705 return -ENOMEM;
706
707 ctx->dcs_read = dcs_read;
708 ctx->dcs_write = dcs_write;
709 dev_set_drvdata(dev, ctx);
710
711 ctx->dev = dev;
712 ctx->enabled = false;
713 ctx->prepared = false;
714
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;
722 }
723
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);
728 }
729
730 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
731 dsi_mode ? DRM_MODE_CONNECTOR_DSI :
732 DRM_MODE_CONNECTOR_DPI);
733
734 ret = s6e63m0_backlight_register(ctx);
735 if (ret < 0)
736 return ret;
737
738 drm_panel_add(&ctx->panel);
739
740 return 0;
741}
742EXPORT_SYMBOL_GPL(s6e63m0_probe);
743
744int s6e63m0_remove(struct device *dev)
745{
746 struct s6e63m0 *ctx = dev_get_drvdata(dev);
747
748 drm_panel_remove(&ctx->panel);
749
750 return 0;
751}
752EXPORT_SYMBOL_GPL(s6e63m0_remove);
753
754MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
755MODULE_DESCRIPTION("s6e63m0 LCD Driver");
756MODULE_LICENSE("GPL v2");