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