Merge tag 'drm-misc-next-fixes-2023-09-01' of git://anongit.freedesktop.org/drm/drm...
[platform/kernel/linux-rpi.git] / drivers / gpu / drm / panel / panel-samsung-s6d7aa0.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Samsung S6D7AA0 MIPI-DSI TFT LCD controller drm_panel driver.
4  *
5  * Copyright (C) 2022 Artur Weber <aweber.kernel@gmail.com>
6  */
7
8 #include <linux/backlight.h>
9 #include <linux/delay.h>
10 #include <linux/gpio/consumer.h>
11 #include <linux/module.h>
12 #include <linux/regulator/consumer.h>
13 #include <linux/of.h>
14
15 #include <video/mipi_display.h>
16 #include <drm/drm_mipi_dsi.h>
17 #include <drm/drm_modes.h>
18 #include <drm/drm_panel.h>
19
20 /* Manufacturer command set */
21 #define MCS_BL_CTL              0xc3
22 #define MCS_OTP_RELOAD          0xd0
23 #define MCS_PASSWD1             0xf0
24 #define MCS_PASSWD2             0xf1
25 #define MCS_PASSWD3             0xfc
26
27 struct s6d7aa0 {
28         struct drm_panel panel;
29         struct mipi_dsi_device *dsi;
30         struct gpio_desc *reset_gpio;
31         struct regulator_bulk_data supplies[2];
32         const struct s6d7aa0_panel_desc *desc;
33 };
34
35 struct s6d7aa0_panel_desc {
36         unsigned int panel_type;
37         int (*init_func)(struct s6d7aa0 *ctx);
38         int (*off_func)(struct s6d7aa0 *ctx);
39         const struct drm_display_mode *drm_mode;
40         unsigned long mode_flags;
41         u32 bus_flags;
42         bool has_backlight;
43         bool use_passwd3;
44 };
45
46 enum s6d7aa0_panels {
47         S6D7AA0_PANEL_LSL080AL02,
48         S6D7AA0_PANEL_LSL080AL03,
49         S6D7AA0_PANEL_LTL101AT01,
50 };
51
52 static inline struct s6d7aa0 *panel_to_s6d7aa0(struct drm_panel *panel)
53 {
54         return container_of(panel, struct s6d7aa0, panel);
55 }
56
57 static void s6d7aa0_reset(struct s6d7aa0 *ctx)
58 {
59         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
60         msleep(50);
61         gpiod_set_value_cansleep(ctx->reset_gpio, 0);
62         msleep(50);
63 }
64
65 static int s6d7aa0_lock(struct s6d7aa0 *ctx, bool lock)
66 {
67         struct mipi_dsi_device *dsi = ctx->dsi;
68
69         if (lock) {
70                 mipi_dsi_dcs_write_seq(dsi, MCS_PASSWD1, 0xa5, 0xa5);
71                 mipi_dsi_dcs_write_seq(dsi, MCS_PASSWD2, 0xa5, 0xa5);
72                 if (ctx->desc->use_passwd3)
73                         mipi_dsi_dcs_write_seq(dsi, MCS_PASSWD3, 0x5a, 0x5a);
74         } else {
75                 mipi_dsi_dcs_write_seq(dsi, MCS_PASSWD1, 0x5a, 0x5a);
76                 mipi_dsi_dcs_write_seq(dsi, MCS_PASSWD2, 0x5a, 0x5a);
77                 if (ctx->desc->use_passwd3)
78                         mipi_dsi_dcs_write_seq(dsi, MCS_PASSWD3, 0xa5, 0xa5);
79         }
80
81         return 0;
82 }
83
84 static int s6d7aa0_on(struct s6d7aa0 *ctx)
85 {
86         struct mipi_dsi_device *dsi = ctx->dsi;
87         struct device *dev = &dsi->dev;
88         int ret;
89
90         ret = ctx->desc->init_func(ctx);
91         if (ret < 0) {
92                 dev_err(dev, "Failed to initialize panel: %d\n", ret);
93                 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
94                 return ret;
95         }
96
97         ret = mipi_dsi_dcs_set_display_on(dsi);
98         if (ret < 0) {
99                 dev_err(dev, "Failed to set display on: %d\n", ret);
100                 return ret;
101         }
102
103         return 0;
104 }
105
106 static int s6d7aa0_off(struct s6d7aa0 *ctx)
107 {
108         struct mipi_dsi_device *dsi = ctx->dsi;
109         struct device *dev = &dsi->dev;
110         int ret;
111
112         ret = ctx->desc->off_func(ctx);
113         if (ret < 0) {
114                 dev_err(dev, "Panel-specific off function failed: %d\n", ret);
115                 return ret;
116         }
117
118         ret = mipi_dsi_dcs_set_display_off(dsi);
119         if (ret < 0) {
120                 dev_err(dev, "Failed to set display off: %d\n", ret);
121                 return ret;
122         }
123         msleep(64);
124
125         ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
126         if (ret < 0) {
127                 dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
128                 return ret;
129         }
130         msleep(120);
131
132         return 0;
133 }
134
135 static int s6d7aa0_prepare(struct drm_panel *panel)
136 {
137         struct s6d7aa0 *ctx = panel_to_s6d7aa0(panel);
138         struct device *dev = &ctx->dsi->dev;
139         int ret;
140
141         ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
142         if (ret < 0) {
143                 dev_err(dev, "Failed to enable regulators: %d\n", ret);
144                 return ret;
145         }
146
147         s6d7aa0_reset(ctx);
148
149         ret = s6d7aa0_on(ctx);
150         if (ret < 0) {
151                 dev_err(dev, "Failed to initialize panel: %d\n", ret);
152                 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
153                 return ret;
154         }
155
156         return 0;
157 }
158
159 static int s6d7aa0_disable(struct drm_panel *panel)
160 {
161         struct s6d7aa0 *ctx = panel_to_s6d7aa0(panel);
162         struct device *dev = &ctx->dsi->dev;
163         int ret;
164
165         ret = s6d7aa0_off(ctx);
166         if (ret < 0)
167                 dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
168
169         return 0;
170 }
171
172 static int s6d7aa0_unprepare(struct drm_panel *panel)
173 {
174         struct s6d7aa0 *ctx = panel_to_s6d7aa0(panel);
175
176         gpiod_set_value_cansleep(ctx->reset_gpio, 1);
177         regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
178
179         return 0;
180 }
181
182 /* Backlight control code */
183
184 static int s6d7aa0_bl_update_status(struct backlight_device *bl)
185 {
186         struct mipi_dsi_device *dsi = bl_get_data(bl);
187         u16 brightness = backlight_get_brightness(bl);
188         int ret;
189
190         ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
191         if (ret < 0)
192                 return ret;
193
194         return 0;
195 }
196
197 static int s6d7aa0_bl_get_brightness(struct backlight_device *bl)
198 {
199         struct mipi_dsi_device *dsi = bl_get_data(bl);
200         u16 brightness;
201         int ret;
202
203         ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
204         if (ret < 0)
205                 return ret;
206
207         return brightness & 0xff;
208 }
209
210 static const struct backlight_ops s6d7aa0_bl_ops = {
211         .update_status = s6d7aa0_bl_update_status,
212         .get_brightness = s6d7aa0_bl_get_brightness,
213 };
214
215 static struct backlight_device *
216 s6d7aa0_create_backlight(struct mipi_dsi_device *dsi)
217 {
218         struct device *dev = &dsi->dev;
219         const struct backlight_properties props = {
220                 .type = BACKLIGHT_RAW,
221                 .brightness = 255,
222                 .max_brightness = 255,
223         };
224
225         return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
226                                               &s6d7aa0_bl_ops, &props);
227 }
228
229 /* Initialization code and structures for LSL080AL02 panel */
230
231 static int s6d7aa0_lsl080al02_init(struct s6d7aa0 *ctx)
232 {
233         struct mipi_dsi_device *dsi = ctx->dsi;
234         struct device *dev = &dsi->dev;
235         int ret;
236
237         usleep_range(20000, 25000);
238
239         ret = s6d7aa0_lock(ctx, false);
240         if (ret < 0) {
241                 dev_err(dev, "Failed to unlock registers: %d\n", ret);
242                 return ret;
243         }
244
245         mipi_dsi_dcs_write_seq(dsi, MCS_OTP_RELOAD, 0x00, 0x10);
246         usleep_range(1000, 1500);
247
248         /* SEQ_B6_PARAM_8_R01 */
249         mipi_dsi_dcs_write_seq(dsi, 0xb6, 0x10);
250
251         /* BL_CTL_ON */
252         mipi_dsi_dcs_write_seq(dsi, MCS_BL_CTL, 0x40, 0x00, 0x28);
253
254         usleep_range(5000, 6000);
255
256         mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x04);
257
258         ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
259         if (ret < 0) {
260                 dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
261                 return ret;
262         }
263
264         msleep(120);
265         mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
266
267         ret = s6d7aa0_lock(ctx, true);
268         if (ret < 0) {
269                 dev_err(dev, "Failed to lock registers: %d\n", ret);
270                 return ret;
271         }
272
273         ret = mipi_dsi_dcs_set_display_on(dsi);
274         if (ret < 0) {
275                 dev_err(dev, "Failed to set display on: %d\n", ret);
276                 return ret;
277         }
278
279         return 0;
280 }
281
282 static int s6d7aa0_lsl080al02_off(struct s6d7aa0 *ctx)
283 {
284         struct mipi_dsi_device *dsi = ctx->dsi;
285
286         /* BL_CTL_OFF */
287         mipi_dsi_dcs_write_seq(dsi, MCS_BL_CTL, 0x40, 0x00, 0x20);
288
289         return 0;
290 }
291
292 static const struct drm_display_mode s6d7aa0_lsl080al02_mode = {
293         .clock = (800 + 16 + 4 + 140) * (1280 + 8 + 4 + 4) * 60 / 1000,
294         .hdisplay = 800,
295         .hsync_start = 800 + 16,
296         .hsync_end = 800 + 16 + 4,
297         .htotal = 800 + 16 + 4 + 140,
298         .vdisplay = 1280,
299         .vsync_start = 1280 + 8,
300         .vsync_end = 1280 + 8 + 4,
301         .vtotal = 1280 + 8 + 4 + 4,
302         .width_mm = 108,
303         .height_mm = 173,
304 };
305
306 static const struct s6d7aa0_panel_desc s6d7aa0_lsl080al02_desc = {
307         .panel_type = S6D7AA0_PANEL_LSL080AL02,
308         .init_func = s6d7aa0_lsl080al02_init,
309         .off_func = s6d7aa0_lsl080al02_off,
310         .drm_mode = &s6d7aa0_lsl080al02_mode,
311         .mode_flags = MIPI_DSI_MODE_VSYNC_FLUSH | MIPI_DSI_MODE_VIDEO_NO_HFP,
312         .bus_flags = DRM_BUS_FLAG_DE_HIGH,
313
314         .has_backlight = false,
315         .use_passwd3 = false,
316 };
317
318 /* Initialization code and structures for LSL080AL03 panel */
319
320 static int s6d7aa0_lsl080al03_init(struct s6d7aa0 *ctx)
321 {
322         struct mipi_dsi_device *dsi = ctx->dsi;
323         struct device *dev = &dsi->dev;
324         int ret;
325
326         usleep_range(20000, 25000);
327
328         ret = s6d7aa0_lock(ctx, false);
329         if (ret < 0) {
330                 dev_err(dev, "Failed to unlock registers: %d\n", ret);
331                 return ret;
332         }
333
334         if (ctx->desc->panel_type == S6D7AA0_PANEL_LSL080AL03) {
335                 mipi_dsi_dcs_write_seq(dsi, MCS_BL_CTL, 0xc7, 0x00, 0x29);
336                 mipi_dsi_dcs_write_seq(dsi, 0xbc, 0x01, 0x4e, 0xa0);
337                 mipi_dsi_dcs_write_seq(dsi, 0xfd, 0x16, 0x10, 0x11, 0x23,
338                                        0x09);
339                 mipi_dsi_dcs_write_seq(dsi, 0xfe, 0x00, 0x02, 0x03, 0x21,
340                                        0x80, 0x78);
341         } else if (ctx->desc->panel_type == S6D7AA0_PANEL_LTL101AT01) {
342                 mipi_dsi_dcs_write_seq(dsi, MCS_BL_CTL, 0x40, 0x00, 0x08);
343                 mipi_dsi_dcs_write_seq(dsi, 0xbc, 0x01, 0x4e, 0x0b);
344                 mipi_dsi_dcs_write_seq(dsi, 0xfd, 0x16, 0x10, 0x11, 0x23,
345                                        0x09);
346                 mipi_dsi_dcs_write_seq(dsi, 0xfe, 0x00, 0x02, 0x03, 0x21,
347                                        0x80, 0x68);
348         }
349
350         mipi_dsi_dcs_write_seq(dsi, 0xb3, 0x51);
351         mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24);
352         mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x02, 0x08, 0x08);
353
354         usleep_range(10000, 11000);
355
356         mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x80, 0x80, 0x30);
357         mipi_dsi_dcs_write_seq(dsi, 0xcd,
358                                0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
359                                0x2e, 0x2e, 0x2e, 0x2e, 0x2e);
360         mipi_dsi_dcs_write_seq(dsi, 0xce,
361                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
362                                0x00, 0x00, 0x00, 0x00, 0x00);
363         mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x03);
364
365         ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
366         if (ret < 0) {
367                 dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
368                 return ret;
369         }
370
371         ret = s6d7aa0_lock(ctx, true);
372         if (ret < 0) {
373                 dev_err(dev, "Failed to lock registers: %d\n", ret);
374                 return ret;
375         }
376
377         ret = mipi_dsi_dcs_set_display_on(dsi);
378         if (ret < 0) {
379                 dev_err(dev, "Failed to set display on: %d\n", ret);
380                 return ret;
381         }
382
383         return 0;
384 }
385
386 static int s6d7aa0_lsl080al03_off(struct s6d7aa0 *ctx)
387 {
388         struct mipi_dsi_device *dsi = ctx->dsi;
389
390         mipi_dsi_dcs_write_seq(dsi, 0x22, 0x00);
391
392         return 0;
393 }
394
395 static const struct drm_display_mode s6d7aa0_lsl080al03_mode = {
396         .clock = (768 + 18 + 16 + 126) * (1024 + 8 + 2 + 6) * 60 / 1000,
397         .hdisplay = 768,
398         .hsync_start = 768 + 18,
399         .hsync_end = 768 + 18 + 16,
400         .htotal = 768 + 18 + 16 + 126,
401         .vdisplay = 1024,
402         .vsync_start = 1024 + 8,
403         .vsync_end = 1024 + 8 + 2,
404         .vtotal = 1024 + 8 + 2 + 6,
405         .width_mm = 122,
406         .height_mm = 163,
407 };
408
409 static const struct s6d7aa0_panel_desc s6d7aa0_lsl080al03_desc = {
410         .panel_type = S6D7AA0_PANEL_LSL080AL03,
411         .init_func = s6d7aa0_lsl080al03_init,
412         .off_func = s6d7aa0_lsl080al03_off,
413         .drm_mode = &s6d7aa0_lsl080al03_mode,
414         .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET,
415         .bus_flags = 0,
416
417         .has_backlight = true,
418         .use_passwd3 = true,
419 };
420
421 /* Initialization structures for LTL101AT01 panel */
422
423 static const struct drm_display_mode s6d7aa0_ltl101at01_mode = {
424         .clock = (768 + 96 + 16 + 184) * (1024 + 8 + 2 + 6) * 60 / 1000,
425         .hdisplay = 768,
426         .hsync_start = 768 + 96,
427         .hsync_end = 768 + 96 + 16,
428         .htotal = 768 + 96 + 16 + 184,
429         .vdisplay = 1024,
430         .vsync_start = 1024 + 8,
431         .vsync_end = 1024 + 8 + 2,
432         .vtotal = 1024 + 8 + 2 + 6,
433         .width_mm = 148,
434         .height_mm = 197,
435 };
436
437 static const struct s6d7aa0_panel_desc s6d7aa0_ltl101at01_desc = {
438         .panel_type = S6D7AA0_PANEL_LTL101AT01,
439         .init_func = s6d7aa0_lsl080al03_init, /* Similar init to LSL080AL03 */
440         .off_func = s6d7aa0_lsl080al03_off,
441         .drm_mode = &s6d7aa0_ltl101at01_mode,
442         .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET,
443         .bus_flags = 0,
444
445         .has_backlight = true,
446         .use_passwd3 = true,
447 };
448
449 static int s6d7aa0_get_modes(struct drm_panel *panel,
450                                         struct drm_connector *connector)
451 {
452         struct drm_display_mode *mode;
453         struct s6d7aa0 *ctx;
454
455         ctx = container_of(panel, struct s6d7aa0, panel);
456         if (!ctx)
457                 return -EINVAL;
458
459         mode = drm_mode_duplicate(connector->dev, ctx->desc->drm_mode);
460         if (!mode)
461                 return -ENOMEM;
462
463         drm_mode_set_name(mode);
464
465         mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
466         connector->display_info.width_mm = mode->width_mm;
467         connector->display_info.height_mm = mode->height_mm;
468         connector->display_info.bus_flags = ctx->desc->bus_flags;
469         drm_mode_probed_add(connector, mode);
470
471         return 1;
472 }
473
474 static const struct drm_panel_funcs s6d7aa0_panel_funcs = {
475         .disable = s6d7aa0_disable,
476         .prepare = s6d7aa0_prepare,
477         .unprepare = s6d7aa0_unprepare,
478         .get_modes = s6d7aa0_get_modes,
479 };
480
481 static int s6d7aa0_probe(struct mipi_dsi_device *dsi)
482 {
483         struct device *dev = &dsi->dev;
484         struct s6d7aa0 *ctx;
485         int ret;
486
487         ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
488         if (!ctx)
489                 return -ENOMEM;
490
491         ctx->desc = of_device_get_match_data(dev);
492         if (!ctx->desc)
493                 return -ENODEV;
494
495         ctx->supplies[0].supply = "power";
496         ctx->supplies[1].supply = "vmipi";
497         ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
498                                               ctx->supplies);
499         if (ret < 0)
500                 return dev_err_probe(dev, ret, "Failed to get regulators\n");
501
502         ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
503         if (IS_ERR(ctx->reset_gpio))
504                 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
505                                      "Failed to get reset-gpios\n");
506
507         ctx->dsi = dsi;
508         mipi_dsi_set_drvdata(dsi, ctx);
509
510         dsi->lanes = 4;
511         dsi->format = MIPI_DSI_FMT_RGB888;
512         dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST
513                 | ctx->desc->mode_flags;
514
515         drm_panel_init(&ctx->panel, dev, &s6d7aa0_panel_funcs,
516                        DRM_MODE_CONNECTOR_DSI);
517         ctx->panel.prepare_prev_first = true;
518
519         ret = drm_panel_of_backlight(&ctx->panel);
520         if (ret)
521                 return dev_err_probe(dev, ret, "Failed to get backlight\n");
522
523         /* Use DSI-based backlight as fallback if available */
524         if (ctx->desc->has_backlight && !ctx->panel.backlight) {
525                 ctx->panel.backlight = s6d7aa0_create_backlight(dsi);
526                 if (IS_ERR(ctx->panel.backlight))
527                         return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
528                                              "Failed to create backlight\n");
529         }
530
531         drm_panel_add(&ctx->panel);
532
533         ret = mipi_dsi_attach(dsi);
534         if (ret < 0) {
535                 dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
536                 drm_panel_remove(&ctx->panel);
537                 return ret;
538         }
539
540         return 0;
541 }
542
543 static void s6d7aa0_remove(struct mipi_dsi_device *dsi)
544 {
545         struct s6d7aa0 *ctx = mipi_dsi_get_drvdata(dsi);
546         int ret;
547
548         ret = mipi_dsi_detach(dsi);
549         if (ret < 0)
550                 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
551
552         drm_panel_remove(&ctx->panel);
553 }
554
555 static const struct of_device_id s6d7aa0_of_match[] = {
556         {
557                 .compatible = "samsung,lsl080al02",
558                 .data = &s6d7aa0_lsl080al02_desc
559         },
560         {
561                 .compatible = "samsung,lsl080al03",
562                 .data = &s6d7aa0_lsl080al03_desc
563         },
564         {
565                 .compatible = "samsung,ltl101at01",
566                 .data = &s6d7aa0_ltl101at01_desc
567         },
568         { /* sentinel */ }
569 };
570 MODULE_DEVICE_TABLE(of, s6d7aa0_of_match);
571
572 static struct mipi_dsi_driver s6d7aa0_driver = {
573         .probe = s6d7aa0_probe,
574         .remove = s6d7aa0_remove,
575         .driver = {
576                 .name = "panel-samsung-s6d7aa0",
577                 .of_match_table = s6d7aa0_of_match,
578         },
579 };
580 module_mipi_dsi_driver(s6d7aa0_driver);
581
582 MODULE_AUTHOR("Artur Weber <aweber.kernel@gmail.com>");
583 MODULE_DESCRIPTION("Samsung S6D7AA0 MIPI-DSI LCD controller driver");
584 MODULE_LICENSE("GPL");