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
20#include <video/mipi_display.h>
21
22#include "panel-samsung-s6e63m0.h"
23
24
25#define MCS_ELVSS_ON 0xb1
26#define MCS_MIECTL1 0xc0
27#define MCS_BCMODE 0xc1
28#define MCS_ERROR_CHECK 0xd5
29#define MCS_READ_ID1 0xda
30#define MCS_READ_ID2 0xdb
31#define MCS_READ_ID3 0xdc
32#define MCS_LEVEL_2_KEY 0xf0
33#define MCS_MTP_KEY 0xf1
34#define MCS_DISCTL 0xf2
35#define MCS_SRCCTL 0xf6
36#define MCS_IFCTL 0xf7
37#define MCS_PANELCTL 0xF8
38#define MCS_PGAMMACTL 0xfa
39
40#define S6E63M0_LCD_ID_VALUE_M2 0xA4
41#define S6E63M0_LCD_ID_VALUE_SM2 0xB4
42#define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6
43
44#define NUM_GAMMA_LEVELS 11
45#define GAMMA_TABLE_COUNT 23
46
47#define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
48
49
50static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
51 { MCS_PGAMMACTL, 0x00,
52 0x18, 0x08, 0x24, 0x78, 0xEC, 0x3D, 0xC8,
53 0xC2, 0xB6, 0xC4, 0xC7, 0xB6, 0xD5, 0xD7,
54 0xCC, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51 },
55 { MCS_PGAMMACTL, 0x00,
56 0x18, 0x08, 0x24, 0x73, 0x4A, 0x3D, 0xC0,
57 0xC2, 0xB1, 0xBB, 0xBE, 0xAC, 0xCE, 0xCF,
58 0xC5, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82 },
59 { MCS_PGAMMACTL, 0x00,
60 0x18, 0x08, 0x24, 0x70, 0x51, 0x3E, 0xBF,
61 0xC1, 0xAF, 0xB9, 0xBC, 0xAB, 0xCC, 0xCC,
62 0xC2, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D },
63 { MCS_PGAMMACTL, 0x00,
64 0x18, 0x08, 0x24, 0x6C, 0x54, 0x3A, 0xBC,
65 0xBF, 0xAC, 0xB7, 0xBB, 0xA9, 0xC9, 0xC9,
66 0xBE, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E },
67 { MCS_PGAMMACTL, 0x00,
68 0x18, 0x08, 0x24, 0x69, 0x54, 0x37, 0xBB,
69 0xBE, 0xAC, 0xB4, 0xB7, 0xA6, 0xC7, 0xC8,
70 0xBC, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB },
71 { MCS_PGAMMACTL, 0x00,
72 0x18, 0x08, 0x24, 0x66, 0x55, 0x34, 0xBA,
73 0xBD, 0xAB, 0xB1, 0xB5, 0xA3, 0xC5, 0xC6,
74 0xB9, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA },
75 { MCS_PGAMMACTL, 0x00,
76 0x18, 0x08, 0x24, 0x63, 0x53, 0x31, 0xB8,
77 0xBC, 0xA9, 0xB0, 0xB5, 0xA2, 0xC4, 0xC4,
78 0xB8, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2 },
79 { MCS_PGAMMACTL, 0x00,
80 0x18, 0x08, 0x24, 0x62, 0x54, 0x30, 0xB9,
81 0xBB, 0xA9, 0xB0, 0xB3, 0xA1, 0xC1, 0xC3,
82 0xB7, 0x00, 0x91, 0x00, 0x95, 0x00, 0xDA },
83 { MCS_PGAMMACTL, 0x00,
84 0x18, 0x08, 0x24, 0x66, 0x58, 0x34, 0xB6,
85 0xBA, 0xA7, 0xAF, 0xB3, 0xA0, 0xC1, 0xC2,
86 0xB7, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1 },
87 { MCS_PGAMMACTL, 0x00,
88 0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xB6,
89 0xBA, 0xA8, 0xAC, 0xB1, 0x9D, 0xC1, 0xC1,
90 0xB7, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6 },
91 { MCS_PGAMMACTL, 0x00,
92 0x18, 0x08, 0x24, 0x5f, 0x50, 0x2d, 0xB6,
93 0xB9, 0xA7, 0xAd, 0xB1, 0x9f, 0xbe, 0xC0,
94 0xB5, 0x00, 0xa0, 0x00, 0xa4, 0x00, 0xdb },
95};
96
97struct s6e63m0 {
98 struct device *dev;
99 int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val);
100 int (*dcs_write)(struct device *dev, const u8 *data, size_t len);
101 struct drm_panel panel;
102 struct backlight_device *bl_dev;
103 u8 lcd_type;
104
105 struct regulator_bulk_data supplies[2];
106 struct gpio_desc *reset_gpio;
107
108 bool prepared;
109 bool enabled;
110
111
112
113
114
115
116
117
118 int error;
119};
120
121static const struct drm_display_mode default_mode = {
122 .clock = 25628,
123 .hdisplay = 480,
124 .hsync_start = 480 + 16,
125 .hsync_end = 480 + 16 + 2,
126 .htotal = 480 + 16 + 2 + 16,
127 .vdisplay = 800,
128 .vsync_start = 800 + 28,
129 .vsync_end = 800 + 28 + 2,
130 .vtotal = 800 + 28 + 2 + 1,
131 .width_mm = 53,
132 .height_mm = 89,
133 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
134};
135
136static inline struct s6e63m0 *panel_to_s6e63m0(struct drm_panel *panel)
137{
138 return container_of(panel, struct s6e63m0, panel);
139}
140
141static int s6e63m0_clear_error(struct s6e63m0 *ctx)
142{
143 int ret = ctx->error;
144
145 ctx->error = 0;
146 return ret;
147}
148
149static void s6e63m0_dcs_read(struct s6e63m0 *ctx, const u8 cmd, u8 *data)
150{
151 if (ctx->error < 0)
152 return;
153
154 ctx->error = ctx->dcs_read(ctx->dev, cmd, data);
155}
156
157static void s6e63m0_dcs_write(struct s6e63m0 *ctx, const u8 *data, size_t len)
158{
159 if (ctx->error < 0 || len == 0)
160 return;
161
162 ctx->error = ctx->dcs_write(ctx->dev, data, len);
163}
164
165#define s6e63m0_dcs_write_seq_static(ctx, seq ...) \
166 ({ \
167 static const u8 d[] = { seq }; \
168 s6e63m0_dcs_write(ctx, d, ARRAY_SIZE(d)); \
169 })
170
171static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
172{
173 u8 id1, id2, id3;
174 int ret;
175
176 s6e63m0_dcs_read(ctx, MCS_READ_ID1, &id1);
177 s6e63m0_dcs_read(ctx, MCS_READ_ID2, &id2);
178 s6e63m0_dcs_read(ctx, MCS_READ_ID3, &id3);
179
180 ret = s6e63m0_clear_error(ctx);
181 if (ret) {
182 dev_err(ctx->dev, "error checking LCD type (%d)\n", ret);
183 ctx->lcd_type = 0x00;
184 return ret;
185 }
186
187 dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
188
189
190 switch (id2) {
191 case S6E63M0_LCD_ID_VALUE_M2:
192 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
193 break;
194 case S6E63M0_LCD_ID_VALUE_SM2:
195 case S6E63M0_LCD_ID_VALUE_SM2_1:
196 dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
197 break;
198 default:
199 dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
200 break;
201 }
202
203 ctx->lcd_type = id2;
204
205 return 0;
206}
207
208static void s6e63m0_init(struct s6e63m0 *ctx)
209{
210 s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
211 0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
212 0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00);
213
214 s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
215 0x02, 0x03, 0x1c, 0x10, 0x10);
216 s6e63m0_dcs_write_seq_static(ctx, MCS_IFCTL,
217 0x03, 0x00, 0x00);
218
219 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
220 0x00, 0x18, 0x08, 0x24, 0x64, 0x56, 0x33,
221 0xb6, 0xba, 0xa8, 0xac, 0xb1, 0x9d, 0xc1,
222 0xc1, 0xb7, 0x00, 0x9c, 0x00, 0x9f, 0x00,
223 0xd6);
224 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL,
225 0x01);
226
227 s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
228 0x00, 0x8c, 0x07);
229 s6e63m0_dcs_write_seq_static(ctx, 0xb3,
230 0xc);
231
232 s6e63m0_dcs_write_seq_static(ctx, 0xb5,
233 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
234 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
235 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
236 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
237 0x21, 0x20, 0x1e, 0x1e);
238
239 s6e63m0_dcs_write_seq_static(ctx, 0xb6,
240 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
241 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
242 0x66, 0x66);
243
244 s6e63m0_dcs_write_seq_static(ctx, 0xb7,
245 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
246 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
247 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
248 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
249 0x21, 0x20, 0x1e, 0x1e, 0x00, 0x00, 0x11,
250 0x22, 0x33, 0x44, 0x44, 0x44, 0x55, 0x55,
251 0x66, 0x66, 0x66, 0x66, 0x66, 0x66);
252
253 s6e63m0_dcs_write_seq_static(ctx, 0xb9,
254 0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
255 0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
256 0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
257 0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
258 0x21, 0x20, 0x1e, 0x1e);
259
260 s6e63m0_dcs_write_seq_static(ctx, 0xba,
261 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
262 0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
263 0x66, 0x66);
264
265 s6e63m0_dcs_write_seq_static(ctx, MCS_BCMODE,
266 0x4d, 0x96, 0x1d, 0x00, 0x00, 0x01, 0xdf,
267 0x00, 0x00, 0x03, 0x1f, 0x00, 0x00, 0x00,
268 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
269 0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
270
271 s6e63m0_dcs_write_seq_static(ctx, 0xb2,
272 0x10, 0x10, 0x0b, 0x05);
273
274 s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
275 0x01);
276
277 s6e63m0_dcs_write_seq_static(ctx, MCS_ELVSS_ON,
278 0x0b);
279}
280
281static int s6e63m0_power_on(struct s6e63m0 *ctx)
282{
283 int ret;
284
285 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
286 if (ret < 0)
287 return ret;
288
289 msleep(25);
290
291
292 gpiod_set_value(ctx->reset_gpio, 1);
293 msleep(5);
294 gpiod_set_value(ctx->reset_gpio, 0);
295 msleep(120);
296
297 return 0;
298}
299
300static int s6e63m0_power_off(struct s6e63m0 *ctx)
301{
302 int ret;
303
304 gpiod_set_value(ctx->reset_gpio, 1);
305 msleep(120);
306
307 ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
308 if (ret < 0)
309 return ret;
310
311 return 0;
312}
313
314static int s6e63m0_disable(struct drm_panel *panel)
315{
316 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
317
318 if (!ctx->enabled)
319 return 0;
320
321 backlight_disable(ctx->bl_dev);
322
323 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF);
324 msleep(10);
325 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
326 msleep(120);
327
328 ctx->enabled = false;
329
330 return 0;
331}
332
333static int s6e63m0_unprepare(struct drm_panel *panel)
334{
335 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
336 int ret;
337
338 if (!ctx->prepared)
339 return 0;
340
341 s6e63m0_clear_error(ctx);
342
343 ret = s6e63m0_power_off(ctx);
344 if (ret < 0)
345 return ret;
346
347 ctx->prepared = false;
348
349 return 0;
350}
351
352static int s6e63m0_prepare(struct drm_panel *panel)
353{
354 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
355 int ret;
356
357 if (ctx->prepared)
358 return 0;
359
360 ret = s6e63m0_power_on(ctx);
361 if (ret < 0)
362 return ret;
363
364
365 s6e63m0_dcs_write_seq_static(ctx, MCS_LEVEL_2_KEY, 0x5a, 0x5a);
366
367 s6e63m0_dcs_write_seq_static(ctx, MCS_MTP_KEY, 0x5a, 0x5a);
368
369 ret = s6e63m0_check_lcd_type(ctx);
370 if (ret < 0)
371 return ret;
372
373 s6e63m0_init(ctx);
374
375 ret = s6e63m0_clear_error(ctx);
376
377 if (ret < 0)
378 s6e63m0_unprepare(panel);
379
380 ctx->prepared = true;
381
382 return ret;
383}
384
385static int s6e63m0_enable(struct drm_panel *panel)
386{
387 struct s6e63m0 *ctx = panel_to_s6e63m0(panel);
388
389 if (ctx->enabled)
390 return 0;
391
392 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE);
393 msleep(120);
394 s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON);
395 msleep(10);
396
397 s6e63m0_dcs_write_seq_static(ctx, MCS_ERROR_CHECK,
398 0xE7, 0x14, 0x60, 0x17, 0x0A, 0x49, 0xC3,
399 0x8F, 0x19, 0x64, 0x91, 0x84, 0x76, 0x20,
400 0x0F, 0x00);
401
402 backlight_enable(ctx->bl_dev);
403
404 ctx->enabled = true;
405
406 return 0;
407}
408
409static int s6e63m0_get_modes(struct drm_panel *panel,
410 struct drm_connector *connector)
411{
412 struct drm_display_mode *mode;
413
414 mode = drm_mode_duplicate(connector->dev, &default_mode);
415 if (!mode) {
416 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
417 default_mode.hdisplay, default_mode.vdisplay,
418 drm_mode_vrefresh(&default_mode));
419 return -ENOMEM;
420 }
421
422 drm_mode_set_name(mode);
423
424 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
425 drm_mode_probed_add(connector, mode);
426
427 return 1;
428}
429
430static const struct drm_panel_funcs s6e63m0_drm_funcs = {
431 .disable = s6e63m0_disable,
432 .unprepare = s6e63m0_unprepare,
433 .prepare = s6e63m0_prepare,
434 .enable = s6e63m0_enable,
435 .get_modes = s6e63m0_get_modes,
436};
437
438static int s6e63m0_set_brightness(struct backlight_device *bd)
439{
440 struct s6e63m0 *ctx = bl_get_data(bd);
441
442 int brightness = bd->props.brightness;
443
444
445 s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
446 ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
447
448
449 s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01);
450
451 return s6e63m0_clear_error(ctx);
452}
453
454static const struct backlight_ops s6e63m0_backlight_ops = {
455 .update_status = s6e63m0_set_brightness,
456};
457
458static int s6e63m0_backlight_register(struct s6e63m0 *ctx)
459{
460 struct backlight_properties props = {
461 .type = BACKLIGHT_RAW,
462 .brightness = MAX_BRIGHTNESS,
463 .max_brightness = MAX_BRIGHTNESS
464 };
465 struct device *dev = ctx->dev;
466 int ret = 0;
467
468 ctx->bl_dev = devm_backlight_device_register(dev, "panel", dev, ctx,
469 &s6e63m0_backlight_ops,
470 &props);
471 if (IS_ERR(ctx->bl_dev)) {
472 ret = PTR_ERR(ctx->bl_dev);
473 dev_err(dev, "error registering backlight device (%d)\n", ret);
474 }
475
476 return ret;
477}
478
479int s6e63m0_probe(struct device *dev,
480 int (*dcs_read)(struct device *dev, const u8 cmd, u8 *val),
481 int (*dcs_write)(struct device *dev, const u8 *data, size_t len),
482 bool dsi_mode)
483{
484 struct s6e63m0 *ctx;
485 int ret;
486
487 ctx = devm_kzalloc(dev, sizeof(struct s6e63m0), GFP_KERNEL);
488 if (!ctx)
489 return -ENOMEM;
490
491 ctx->dcs_read = dcs_read;
492 ctx->dcs_write = dcs_write;
493 dev_set_drvdata(dev, ctx);
494
495 ctx->dev = dev;
496 ctx->enabled = false;
497 ctx->prepared = false;
498
499 ctx->supplies[0].supply = "vdd3";
500 ctx->supplies[1].supply = "vci";
501 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
502 ctx->supplies);
503 if (ret < 0) {
504 dev_err(dev, "failed to get regulators: %d\n", ret);
505 return ret;
506 }
507
508 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
509 if (IS_ERR(ctx->reset_gpio)) {
510 dev_err(dev, "cannot get reset-gpios %ld\n", PTR_ERR(ctx->reset_gpio));
511 return PTR_ERR(ctx->reset_gpio);
512 }
513
514 drm_panel_init(&ctx->panel, dev, &s6e63m0_drm_funcs,
515 dsi_mode ? DRM_MODE_CONNECTOR_DSI :
516 DRM_MODE_CONNECTOR_DPI);
517
518 ret = s6e63m0_backlight_register(ctx);
519 if (ret < 0)
520 return ret;
521
522 drm_panel_add(&ctx->panel);
523
524 return 0;
525}
526EXPORT_SYMBOL_GPL(s6e63m0_probe);
527
528int s6e63m0_remove(struct device *dev)
529{
530 struct s6e63m0 *ctx = dev_get_drvdata(dev);
531
532 drm_panel_remove(&ctx->panel);
533
534 return 0;
535}
536EXPORT_SYMBOL_GPL(s6e63m0_remove);
537
538MODULE_AUTHOR("Paweł Chmiel <pawel.mikolaj.chmiel@gmail.com>");
539MODULE_DESCRIPTION("s6e63m0 LCD Driver");
540MODULE_LICENSE("GPL v2");
541