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 void (*init)(struct mipi_dsi_multi_context *dsi_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;
41 static const struct ltk050h3146w_cmd page1_cmds[] = {
42 { 0x22, 0x0A }, /* BGR SS GS */
43 { 0x31, 0x00 }, /* column inversion */
44 { 0x53, 0xA2 }, /* VCOM1 */
45 { 0x55, 0xA2 }, /* VCOM2 */
46 { 0x50, 0x81 }, /* VREG1OUT=5V */
47 { 0x51, 0x85 }, /* VREG2OUT=-5V */
48 { 0x62, 0x0D }, /* EQT Time setting */
50 * The vendor init selected page 1 here _again_
51 * Is this supposed to be page 2?
95 static const struct ltk050h3146w_cmd page3_cmds[] = {
225 static const struct ltk050h3146w_cmd page4_cmds[] = {
228 { 0x82, 0x0F }, /* VGH_MOD clamp level=15v */
229 { 0x84, 0x0F }, /* VGH clamp level 15V */
230 { 0x85, 0x0D }, /* VGL clamp level (-10V) */
234 { 0xB5, 0x07 }, /* GAMMA OP */
235 { 0x31, 0x45 }, /* SOURCE OP */
236 { 0x3A, 0x24 }, /* PS_EN OFF */
237 { 0x88, 0x33 }, /* LVD */
241 struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
243 return container_of(panel, struct ltk050h3146w, panel);
246 static void ltk050h3148w_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
249 * Init sequence was supplied by the panel vendor without much
252 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0xff, 0x83, 0x94);
253 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb1, 0x50, 0x15, 0x75, 0x09, 0x32, 0x44,
254 0x71, 0x31, 0x55, 0x2f);
255 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xba, 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
256 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x88);
257 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x00, 0x80, 0x64, 0x10, 0x07);
258 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb4, 0x05, 0x70, 0x05, 0x70, 0x01, 0x70,
259 0x01, 0x0c, 0x86, 0x75, 0x00, 0x3f, 0x01, 0x74,
260 0x01, 0x74, 0x01, 0x74, 0x01, 0x0c, 0x86);
261 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd3, 0x00, 0x00, 0x07, 0x07, 0x40, 0x1e,
262 0x08, 0x00, 0x32, 0x10, 0x08, 0x00, 0x08, 0x54,
263 0x15, 0x10, 0x05, 0x04, 0x02, 0x12, 0x10, 0x05,
264 0x07, 0x33, 0x34, 0x0c, 0x0c, 0x37, 0x10, 0x07,
266 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd5, 0x19, 0x19, 0x18, 0x18, 0x1b, 0x1b,
267 0x1a, 0x1a, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01,
268 0x02, 0x03, 0x20, 0x21, 0x18, 0x18, 0x22, 0x23,
269 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
270 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
271 0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
272 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd6, 0x18, 0x18, 0x19, 0x19, 0x1b, 0x1b,
273 0x1a, 0x1a, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06,
274 0x05, 0x04, 0x23, 0x22, 0x18, 0x18, 0x21, 0x20,
275 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
276 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
277 0x18, 0x18, 0x18, 0x18, 0x18, 0x18);
278 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe0, 0x00, 0x03, 0x09, 0x11, 0x11, 0x14,
279 0x18, 0x16, 0x2e, 0x3d, 0x4d, 0x4d, 0x58, 0x6c,
280 0x72, 0x78, 0x88, 0x8b, 0x86, 0xa4, 0xb2, 0x58,
281 0x55, 0x59, 0x5b, 0x5d, 0x60, 0x64, 0x7f, 0x00,
282 0x03, 0x09, 0x0f, 0x11, 0x14, 0x18, 0x16, 0x2e,
283 0x3d, 0x4d, 0x4d, 0x58, 0x6d, 0x73, 0x78, 0x88,
284 0x8b, 0x87, 0xa5, 0xb2, 0x58, 0x55, 0x58, 0x5b,
285 0x5d, 0x61, 0x65, 0x7f);
286 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xcc, 0x0b);
287 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x1f, 0x31);
288 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb6, 0xc4, 0xc4);
289 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x01);
290 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb1, 0x00);
291 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x00);
292 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc6, 0xef);
293 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd4, 0x02);
295 mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 1);
296 mipi_dsi_msleep(dsi_ctx, 60);
299 static const struct drm_display_mode ltk050h3148w_mode = {
301 .hsync_start = 720 + 12,
302 .hsync_end = 720 + 12 + 6,
303 .htotal = 720 + 12 + 6 + 24,
305 .vsync_start = 1280 + 9,
306 .vsync_end = 1280 + 9 + 2,
307 .vtotal = 1280 + 9 + 2 + 16,
313 static const struct ltk050h3146w_desc ltk050h3148w_data = {
314 .mode = <k050h3148w_mode,
315 .init = ltk050h3148w_init_sequence,
316 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
317 MIPI_DSI_MODE_VIDEO_BURST,
320 static void ltk050h3146w_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
323 * Init sequence was supplied by the panel vendor without much
326 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdf, 0x93, 0x65, 0xf8);
327 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
329 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x00, 0xb5);
330 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb3, 0x00, 0xb5);
331 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
333 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0x00, 0xc4, 0x23, 0x07);
334 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
335 0x28, 0x04, 0xcc, 0xcc, 0xcc);
336 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbc, 0x0f, 0x04);
337 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbe, 0x1e, 0xf2);
338 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x26, 0x03);
339 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x00, 0x12);
340 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
342 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
344 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
345 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
346 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
347 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
348 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
349 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
350 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
351 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
352 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
353 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
354 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
355 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
356 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
357 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
358 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
359 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
360 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
361 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
362 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
363 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
364 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
365 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
367 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdd, 0x2c, 0xa3, 0x00);
368 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xde, 0x02);
369 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb2, 0x32, 0x1c);
370 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x3b, 0x70, 0x00, 0x04);
371 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x11);
372 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
373 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc2, 0x20, 0x38, 0x1e, 0x84);
374 mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xde, 0x00);
376 mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 1);
377 mipi_dsi_msleep(dsi_ctx, 60);
380 static const struct drm_display_mode ltk050h3146w_mode = {
382 .hsync_start = 720 + 42,
383 .hsync_end = 720 + 42 + 8,
384 .htotal = 720 + 42 + 8 + 42,
386 .vsync_start = 1280 + 12,
387 .vsync_end = 1280 + 12 + 4,
388 .vtotal = 1280 + 12 + 4 + 18,
394 static const struct ltk050h3146w_desc ltk050h3146w_data = {
395 .mode = <k050h3146w_mode,
396 .init = ltk050h3146w_init_sequence,
397 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
398 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
401 static void ltk050h3146w_a2_select_page(struct mipi_dsi_multi_context *dsi_ctx, int page)
403 u8 d[4] = { 0xff, 0x98, 0x81, page };
405 mipi_dsi_dcs_write_buffer_multi(dsi_ctx, d, ARRAY_SIZE(d));
408 static void ltk050h3146w_a2_write_page(struct mipi_dsi_multi_context *dsi_ctx, int page,
409 const struct ltk050h3146w_cmd *cmds,
412 ltk050h3146w_a2_select_page(dsi_ctx, page);
414 for (int i = 0; i < num; i++)
415 mipi_dsi_generic_write_multi(dsi_ctx, &cmds[i],
416 sizeof(struct ltk050h3146w_cmd));
419 static void ltk050h3146w_a2_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
422 * Init sequence was supplied by the panel vendor without much
425 ltk050h3146w_a2_write_page(dsi_ctx, 3, page3_cmds,
426 ARRAY_SIZE(page3_cmds));
427 ltk050h3146w_a2_write_page(dsi_ctx, 4, page4_cmds,
428 ARRAY_SIZE(page4_cmds));
429 ltk050h3146w_a2_write_page(dsi_ctx, 1, page1_cmds,
430 ARRAY_SIZE(page1_cmds));
431 ltk050h3146w_a2_select_page(dsi_ctx, 0);
433 /* vendor code called this without param, where there should be one */
434 mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, 0);
436 mipi_dsi_msleep(dsi_ctx, 60);
439 static const struct drm_display_mode ltk050h3146w_a2_mode = {
441 .hsync_start = 720 + 42,
442 .hsync_end = 720 + 42 + 10,
443 .htotal = 720 + 42 + 10 + 60,
445 .vsync_start = 1280 + 18,
446 .vsync_end = 1280 + 18 + 4,
447 .vtotal = 1280 + 18 + 4 + 12,
453 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
454 .mode = <k050h3146w_a2_mode,
455 .init = ltk050h3146w_a2_init_sequence,
456 .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
457 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
460 static int ltk050h3146w_unprepare(struct drm_panel *panel)
462 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
463 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
464 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
466 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
467 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
468 if (dsi_ctx.accum_err)
469 return dsi_ctx.accum_err;
471 regulator_disable(ctx->iovcc);
472 regulator_disable(ctx->vci);
477 static int ltk050h3146w_prepare(struct drm_panel *panel)
479 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
480 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
481 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
483 dev_dbg(ctx->dev, "Resetting the panel\n");
484 dsi_ctx.accum_err = regulator_enable(ctx->vci);
485 if (dsi_ctx.accum_err) {
486 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", dsi_ctx.accum_err);
487 return dsi_ctx.accum_err;
489 dsi_ctx.accum_err = regulator_enable(ctx->iovcc);
490 if (dsi_ctx.accum_err) {
491 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", dsi_ctx.accum_err);
495 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
496 usleep_range(5000, 6000);
497 gpiod_set_value_cansleep(ctx->reset_gpio, 0);
500 ctx->panel_desc->init(&dsi_ctx);
501 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
503 mipi_dsi_msleep(&dsi_ctx, 120);
504 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
505 mipi_dsi_msleep(&dsi_ctx, 50);
507 if (dsi_ctx.accum_err)
513 regulator_disable(ctx->iovcc);
515 regulator_disable(ctx->vci);
516 return dsi_ctx.accum_err;
519 static int ltk050h3146w_get_modes(struct drm_panel *panel,
520 struct drm_connector *connector)
522 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
523 struct drm_display_mode *mode;
525 mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
529 drm_mode_set_name(mode);
531 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
532 connector->display_info.width_mm = mode->width_mm;
533 connector->display_info.height_mm = mode->height_mm;
534 drm_mode_probed_add(connector, mode);
539 static const struct drm_panel_funcs ltk050h3146w_funcs = {
540 .unprepare = ltk050h3146w_unprepare,
541 .prepare = ltk050h3146w_prepare,
542 .get_modes = ltk050h3146w_get_modes,
545 static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
547 struct device *dev = &dsi->dev;
548 struct ltk050h3146w *ctx;
551 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
555 ctx->panel_desc = of_device_get_match_data(dev);
556 if (!ctx->panel_desc)
559 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
560 if (IS_ERR(ctx->reset_gpio))
561 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "cannot get reset gpio\n");
563 ctx->vci = devm_regulator_get(dev, "vci");
564 if (IS_ERR(ctx->vci))
565 return dev_err_probe(dev, PTR_ERR(ctx->vci), "Failed to request vci regulator\n");
567 ctx->iovcc = devm_regulator_get(dev, "iovcc");
568 if (IS_ERR(ctx->iovcc))
569 return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
570 "Failed to request iovcc regulator\n");
572 mipi_dsi_set_drvdata(dsi, ctx);
577 dsi->format = MIPI_DSI_FMT_RGB888;
578 dsi->mode_flags = ctx->panel_desc->mode_flags;
580 drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs,
581 DRM_MODE_CONNECTOR_DSI);
583 ret = drm_panel_of_backlight(&ctx->panel);
587 drm_panel_add(&ctx->panel);
589 ret = mipi_dsi_attach(dsi);
591 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
592 drm_panel_remove(&ctx->panel);
599 static void ltk050h3146w_remove(struct mipi_dsi_device *dsi)
601 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
604 ret = mipi_dsi_detach(dsi);
606 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
608 drm_panel_remove(&ctx->panel);
611 static const struct of_device_id ltk050h3146w_of_match[] = {
613 .compatible = "leadtek,ltk050h3146w",
614 .data = <k050h3146w_data,
617 .compatible = "leadtek,ltk050h3146w-a2",
618 .data = <k050h3146w_a2_data,
621 .compatible = "leadtek,ltk050h3148w",
622 .data = <k050h3148w_data,
626 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
628 static struct mipi_dsi_driver ltk050h3146w_driver = {
630 .name = "panel-leadtek-ltk050h3146w",
631 .of_match_table = ltk050h3146w_of_match,
633 .probe = ltk050h3146w_probe,
634 .remove = ltk050h3146w_remove,
636 module_mipi_dsi_driver(ltk050h3146w_driver);
639 MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
640 MODULE_LICENSE("GPL v2");