1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH
6 #include <linux/delay.h>
7 #include <linux/gpio/consumer.h>
8 #include <linux/media-bus-format.h>
9 #include <linux/module.h>
11 #include <linux/regulator/consumer.h>
13 #include <video/display_timing.h>
14 #include <video/mipi_display.h>
16 #include <drm/drm_mipi_dsi.h>
17 #include <drm/drm_modes.h>
18 #include <drm/drm_panel.h>
20 struct ltk050h3146w_cmd {
26 struct ltk050h3146w_desc {
27 const unsigned long mode_flags;
28 const struct drm_display_mode *mode;
29 int (*init)(struct ltk050h3146w *ctx);
34 struct drm_panel panel;
35 struct gpio_desc *reset_gpio;
36 struct regulator *vci;
37 struct regulator *iovcc;
38 const struct ltk050h3146w_desc *panel_desc;
42 static const struct ltk050h3146w_cmd page1_cmds[] = {
43 { 0x22, 0x0A }, /* BGR SS GS */
44 { 0x31, 0x00 }, /* column inversion */
45 { 0x53, 0xA2 }, /* VCOM1 */
46 { 0x55, 0xA2 }, /* VCOM2 */
47 { 0x50, 0x81 }, /* VREG1OUT=5V */
48 { 0x51, 0x85 }, /* VREG2OUT=-5V */
49 { 0x62, 0x0D }, /* EQT Time setting */
51 * The vendor init selected page 1 here _again_
52 * Is this supposed to be page 2?
96 static const struct ltk050h3146w_cmd page3_cmds[] = {
226 static const struct ltk050h3146w_cmd page4_cmds[] = {
229 { 0x82, 0x0F }, /* VGH_MOD clamp level=15v */
230 { 0x84, 0x0F }, /* VGH clamp level 15V */
231 { 0x85, 0x0D }, /* VGL clamp level (-10V) */
235 { 0xB5, 0x07 }, /* GAMMA OP */
236 { 0x31, 0x45 }, /* SOURCE OP */
237 { 0x3A, 0x24 }, /* PS_EN OFF */
238 { 0x88, 0x33 }, /* LVD */
242 struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
244 return container_of(panel, struct ltk050h3146w, panel);
247 static int ltk050h3148w_init_sequence(struct ltk050h3146w *ctx)
249 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
253 * Init sequence was supplied by the panel vendor without much
256 mipi_dsi_dcs_write_seq(dsi, 0xb9, 0xff, 0x83, 0x94);
257 mipi_dsi_dcs_write_seq(dsi, 0xb1, 0x50, 0x15, 0x75, 0x09, 0x32, 0x44,
258 0x71, 0x31, 0x55, 0x2f);
259 mipi_dsi_dcs_write_seq(dsi, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
260 mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x88);
261 mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0x80, 0x64, 0x10, 0x07);
262 mipi_dsi_dcs_write_seq(dsi, 0xb4, 0x05, 0x70, 0x05, 0x70, 0x01, 0x70,
263 0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f, 0x01, 0x74,
264 0x01, 0x74, 0x01, 0x74, 0x01, 0x0c, 0x86);
265 mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x00, 0x00, 0x07, 0x07, 0x40, 0x1e,
266 0x08, 0x00, 0x32, 0x10, 0x08, 0x00, 0x08, 0x54,
267 0x15, 0x10, 0x05, 0x04, 0x02, 0x12, 0x10, 0x05,
268 0x07, 0x33, 0x34, 0x0c, 0x0c, 0x37, 0x10, 0x07,
270 mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b,
271 0x1a, 0x1a, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01,
272 0x02, 0x03, 0x20, 0x21, 0x18, 0x18, 0x22, 0x23,
273 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
274 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
275 0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
276 mipi_dsi_dcs_write_seq(dsi, 0xd6, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b,
277 0x1a, 0x1a, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06,
278 0x05, 0x04, 0x23, 0x22, 0x18, 0x18, 0x21, 0x20,
279 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
280 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
281 0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
282 mipi_dsi_dcs_write_seq(dsi, 0xe0, 0x00, 0x03, 0x09, 0x11, 0x11, 0x14,
283 0x18, 0x16, 0x2e, 0x3d, 0x4d, 0x4d, 0x58, 0x6c,
284 0x72, 0x78, 0x88, 0x8b, 0x86, 0xa4, 0xb2, 0x58,
285 0x55, 0x59, 0x5b, 0x5d, 0x60, 0x64, 0x7f, 0x00,
286 0x03, 0x09, 0x0f, 0x11, 0x14, 0x18, 0x16, 0x2e,
287 0x3d, 0x4d, 0x4d, 0x58, 0x6d, 0x73, 0x78, 0x88,
288 0x8b, 0x87, 0xa5, 0xb2, 0x58, 0x55, 0x58, 0x5b,
289 0x5d, 0x61, 0x65, 0x7f);
290 mipi_dsi_dcs_write_seq(dsi, 0xcc, 0x0b);
291 mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x1f, 0x31);
292 mipi_dsi_dcs_write_seq(dsi, 0xb6, 0xc4, 0xc4);
293 mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x01);
294 mipi_dsi_dcs_write_seq(dsi, 0xb1, 0x00);
295 mipi_dsi_dcs_write_seq(dsi, 0xbd, 0x00);
296 mipi_dsi_dcs_write_seq(dsi, 0xc6, 0xef);
297 mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x02);
298 mipi_dsi_dcs_write_seq(dsi, 0x11);
299 mipi_dsi_dcs_write_seq(dsi, 0x29);
301 ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
303 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
312 static const struct drm_display_mode ltk050h3148w_mode = {
314 .hsync_start = 720 + 12,
315 .hsync_end = 720 + 12 + 6,
316 .htotal = 720 + 12 + 6 + 24,
318 .vsync_start = 1280 + 9,
319 .vsync_end = 1280 + 9 + 2,
320 .vtotal = 1280 + 9 + 2 + 16,
326 static const struct ltk050h3146w_desc ltk050h3148w_data = {
327 .mode = <k050h3148w_mode,
328 .init = ltk050h3148w_init_sequence,
329 .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_VIDEO_BURST,
332 static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
334 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
338 * Init sequence was supplied by the panel vendor without much
341 mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);
342 mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
344 mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);
345 mipi_dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);
346 mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
348 mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);
349 mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
350 0x28, 0x04, 0xcc, 0xcc, 0xcc);
351 mipi_dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);
352 mipi_dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);
353 mipi_dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);
354 mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);
355 mipi_dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
357 mipi_dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
359 mipi_dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
360 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
361 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
362 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
363 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
364 mipi_dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
365 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
366 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
367 mipi_dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
368 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
369 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
370 mipi_dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
371 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
372 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
373 mipi_dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
374 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
375 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
376 mipi_dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
377 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
378 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
379 mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
380 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
382 mipi_dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);
383 mipi_dsi_dcs_write_seq(dsi, 0xde, 0x02);
384 mipi_dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);
385 mipi_dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);
386 mipi_dsi_dcs_write_seq(dsi, 0xc1, 0x11);
387 mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
388 mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);
389 mipi_dsi_dcs_write_seq(dsi, 0xde, 0x00);
391 ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
393 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
402 static const struct drm_display_mode ltk050h3146w_mode = {
404 .hsync_start = 720 + 42,
405 .hsync_end = 720 + 42 + 8,
406 .htotal = 720 + 42 + 8 + 42,
408 .vsync_start = 1280 + 12,
409 .vsync_end = 1280 + 12 + 4,
410 .vtotal = 1280 + 12 + 4 + 18,
416 static const struct ltk050h3146w_desc ltk050h3146w_data = {
417 .mode = <k050h3146w_mode,
418 .init = ltk050h3146w_init_sequence,
419 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
420 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
423 static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
425 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
426 u8 d[3] = { 0x98, 0x81, page };
428 return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
431 static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
432 const struct ltk050h3146w_cmd *cmds,
435 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
438 ret = ltk050h3146w_a2_select_page(ctx, page);
440 dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret);
444 for (i = 0; i < num; i++) {
445 ret = mipi_dsi_generic_write(dsi, &cmds[i],
446 sizeof(struct ltk050h3146w_cmd));
448 dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret);
456 static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
458 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
462 * Init sequence was supplied by the panel vendor without much
465 ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
466 ARRAY_SIZE(page3_cmds));
470 ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
471 ARRAY_SIZE(page4_cmds));
475 ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
476 ARRAY_SIZE(page1_cmds));
480 ret = ltk050h3146w_a2_select_page(ctx, 0);
482 dev_err(ctx->dev, "failed to select page 0: %d\n", ret);
486 /* vendor code called this without param, where there should be one */
487 ret = mipi_dsi_dcs_set_tear_on(dsi, 0);
489 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
498 static const struct drm_display_mode ltk050h3146w_a2_mode = {
500 .hsync_start = 720 + 42,
501 .hsync_end = 720 + 42 + 10,
502 .htotal = 720 + 42 + 10 + 60,
504 .vsync_start = 1280 + 18,
505 .vsync_end = 1280 + 18 + 4,
506 .vtotal = 1280 + 18 + 4 + 12,
512 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
513 .mode = <k050h3146w_a2_mode,
514 .init = ltk050h3146w_a2_init_sequence,
515 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
516 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
519 static int ltk050h3146w_unprepare(struct drm_panel *panel)
521 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
522 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
528 ret = mipi_dsi_dcs_set_display_off(dsi);
530 dev_err(ctx->dev, "failed to set display off: %d\n", ret);
534 mipi_dsi_dcs_enter_sleep_mode(dsi);
536 dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
540 regulator_disable(ctx->iovcc);
541 regulator_disable(ctx->vci);
543 ctx->prepared = false;
548 static int ltk050h3146w_prepare(struct drm_panel *panel)
550 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
551 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
557 dev_dbg(ctx->dev, "Resetting the panel\n");
558 ret = regulator_enable(ctx->vci);
560 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
563 ret = regulator_enable(ctx->iovcc);
565 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
569 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
570 usleep_range(5000, 6000);
571 gpiod_set_value_cansleep(ctx->reset_gpio, 0);
574 ret = ctx->panel_desc->init(ctx);
576 dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
580 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
582 dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
589 ret = mipi_dsi_dcs_set_display_on(dsi);
591 dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
597 ctx->prepared = true;
602 regulator_disable(ctx->iovcc);
604 regulator_disable(ctx->vci);
608 static int ltk050h3146w_get_modes(struct drm_panel *panel,
609 struct drm_connector *connector)
611 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
612 struct drm_display_mode *mode;
614 mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
618 drm_mode_set_name(mode);
620 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
621 connector->display_info.width_mm = mode->width_mm;
622 connector->display_info.height_mm = mode->height_mm;
623 drm_mode_probed_add(connector, mode);
628 static const struct drm_panel_funcs ltk050h3146w_funcs = {
629 .unprepare = ltk050h3146w_unprepare,
630 .prepare = ltk050h3146w_prepare,
631 .get_modes = ltk050h3146w_get_modes,
634 static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
636 struct device *dev = &dsi->dev;
637 struct ltk050h3146w *ctx;
640 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
644 ctx->panel_desc = of_device_get_match_data(dev);
645 if (!ctx->panel_desc)
648 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
649 if (IS_ERR(ctx->reset_gpio))
650 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "cannot get reset gpio\n");
652 ctx->vci = devm_regulator_get(dev, "vci");
653 if (IS_ERR(ctx->vci))
654 return dev_err_probe(dev, PTR_ERR(ctx->vci), "Failed to request vci regulator\n");
656 ctx->iovcc = devm_regulator_get(dev, "iovcc");
657 if (IS_ERR(ctx->iovcc))
658 return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
659 "Failed to request iovcc regulator\n");
661 mipi_dsi_set_drvdata(dsi, ctx);
666 dsi->format = MIPI_DSI_FMT_RGB888;
667 dsi->mode_flags = ctx->panel_desc->mode_flags;
669 drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs,
670 DRM_MODE_CONNECTOR_DSI);
672 ret = drm_panel_of_backlight(&ctx->panel);
676 drm_panel_add(&ctx->panel);
678 ret = mipi_dsi_attach(dsi);
680 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
681 drm_panel_remove(&ctx->panel);
688 static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
690 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
693 ret = drm_panel_unprepare(&ctx->panel);
695 dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
697 ret = drm_panel_disable(&ctx->panel);
699 dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
702 static void ltk050h3146w_remove(struct mipi_dsi_device *dsi)
704 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
707 ltk050h3146w_shutdown(dsi);
709 ret = mipi_dsi_detach(dsi);
711 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
713 drm_panel_remove(&ctx->panel);
716 static const struct of_device_id ltk050h3146w_of_match[] = {
718 .compatible = "leadtek,ltk050h3146w",
719 .data = <k050h3146w_data,
722 .compatible = "leadtek,ltk050h3146w-a2",
723 .data = <k050h3146w_a2_data,
726 .compatible = "leadtek,ltk050h3148w",
727 .data = <k050h3148w_data,
731 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
733 static struct mipi_dsi_driver ltk050h3146w_driver = {
735 .name = "panel-leadtek-ltk050h3146w",
736 .of_match_table = ltk050h3146w_of_match,
738 .probe = ltk050h3146w_probe,
739 .remove = ltk050h3146w_remove,
740 .shutdown = ltk050h3146w_shutdown,
742 module_mipi_dsi_driver(ltk050h3146w_driver);
744 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
745 MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
746 MODULE_LICENSE("GPL v2");