16ea4383bSHeiko Stuebner // SPDX-License-Identifier: GPL-2.0 26ea4383bSHeiko Stuebner /* 36ea4383bSHeiko Stuebner * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH 46ea4383bSHeiko Stuebner */ 56ea4383bSHeiko Stuebner 66ea4383bSHeiko Stuebner #include <linux/delay.h> 76ea4383bSHeiko Stuebner #include <linux/gpio/consumer.h> 86ea4383bSHeiko Stuebner #include <linux/media-bus-format.h> 96ea4383bSHeiko Stuebner #include <linux/module.h> 106ea4383bSHeiko Stuebner #include <linux/of.h> 116ea4383bSHeiko Stuebner #include <linux/of_device.h> 126ea4383bSHeiko Stuebner #include <linux/regulator/consumer.h> 136ea4383bSHeiko Stuebner 146ea4383bSHeiko Stuebner #include <video/display_timing.h> 156ea4383bSHeiko Stuebner #include <video/mipi_display.h> 166ea4383bSHeiko Stuebner 176ea4383bSHeiko Stuebner #include <drm/drm_mipi_dsi.h> 186ea4383bSHeiko Stuebner #include <drm/drm_modes.h> 196ea4383bSHeiko Stuebner #include <drm/drm_panel.h> 206ea4383bSHeiko Stuebner 216ea4383bSHeiko Stuebner struct ltk050h3146w_cmd { 226ea4383bSHeiko Stuebner char cmd; 236ea4383bSHeiko Stuebner char data; 246ea4383bSHeiko Stuebner }; 256ea4383bSHeiko Stuebner 266ea4383bSHeiko Stuebner struct ltk050h3146w; 276ea4383bSHeiko Stuebner struct ltk050h3146w_desc { 286ea4383bSHeiko Stuebner const struct drm_display_mode *mode; 296ea4383bSHeiko Stuebner int (*init)(struct ltk050h3146w *ctx); 306ea4383bSHeiko Stuebner }; 316ea4383bSHeiko Stuebner 326ea4383bSHeiko Stuebner struct ltk050h3146w { 336ea4383bSHeiko Stuebner struct device *dev; 346ea4383bSHeiko Stuebner struct drm_panel panel; 356ea4383bSHeiko Stuebner struct gpio_desc *reset_gpio; 366ea4383bSHeiko Stuebner struct regulator *vci; 376ea4383bSHeiko Stuebner struct regulator *iovcc; 386ea4383bSHeiko Stuebner const struct ltk050h3146w_desc *panel_desc; 396ea4383bSHeiko Stuebner bool prepared; 406ea4383bSHeiko Stuebner }; 416ea4383bSHeiko Stuebner 426ea4383bSHeiko Stuebner static const struct ltk050h3146w_cmd page1_cmds[] = { 436ea4383bSHeiko Stuebner { 0x22, 0x0A }, /* BGR SS GS */ 446ea4383bSHeiko Stuebner { 0x31, 0x00 }, /* column inversion */ 456ea4383bSHeiko Stuebner { 0x53, 0xA2 }, /* VCOM1 */ 466ea4383bSHeiko Stuebner { 0x55, 0xA2 }, /* VCOM2 */ 476ea4383bSHeiko Stuebner { 0x50, 0x81 }, /* VREG1OUT=5V */ 486ea4383bSHeiko Stuebner { 0x51, 0x85 }, /* VREG2OUT=-5V */ 496ea4383bSHeiko Stuebner { 0x62, 0x0D }, /* EQT Time setting */ 506ea4383bSHeiko Stuebner /* 516ea4383bSHeiko Stuebner * The vendor init selected page 1 here _again_ 526ea4383bSHeiko Stuebner * Is this supposed to be page 2? 536ea4383bSHeiko Stuebner */ 546ea4383bSHeiko Stuebner { 0xA0, 0x00 }, 556ea4383bSHeiko Stuebner { 0xA1, 0x1A }, 566ea4383bSHeiko Stuebner { 0xA2, 0x28 }, 576ea4383bSHeiko Stuebner { 0xA3, 0x13 }, 586ea4383bSHeiko Stuebner { 0xA4, 0x16 }, 596ea4383bSHeiko Stuebner { 0xA5, 0x29 }, 606ea4383bSHeiko Stuebner { 0xA6, 0x1D }, 616ea4383bSHeiko Stuebner { 0xA7, 0x1E }, 626ea4383bSHeiko Stuebner { 0xA8, 0x84 }, 636ea4383bSHeiko Stuebner { 0xA9, 0x1C }, 646ea4383bSHeiko Stuebner { 0xAA, 0x28 }, 656ea4383bSHeiko Stuebner { 0xAB, 0x75 }, 666ea4383bSHeiko Stuebner { 0xAC, 0x1A }, 676ea4383bSHeiko Stuebner { 0xAD, 0x19 }, 686ea4383bSHeiko Stuebner { 0xAE, 0x4D }, 696ea4383bSHeiko Stuebner { 0xAF, 0x22 }, 706ea4383bSHeiko Stuebner { 0xB0, 0x28 }, 716ea4383bSHeiko Stuebner { 0xB1, 0x54 }, 726ea4383bSHeiko Stuebner { 0xB2, 0x66 }, 736ea4383bSHeiko Stuebner { 0xB3, 0x39 }, 746ea4383bSHeiko Stuebner { 0xC0, 0x00 }, 756ea4383bSHeiko Stuebner { 0xC1, 0x1A }, 766ea4383bSHeiko Stuebner { 0xC2, 0x28 }, 776ea4383bSHeiko Stuebner { 0xC3, 0x13 }, 786ea4383bSHeiko Stuebner { 0xC4, 0x16 }, 796ea4383bSHeiko Stuebner { 0xC5, 0x29 }, 806ea4383bSHeiko Stuebner { 0xC6, 0x1D }, 816ea4383bSHeiko Stuebner { 0xC7, 0x1E }, 826ea4383bSHeiko Stuebner { 0xC8, 0x84 }, 836ea4383bSHeiko Stuebner { 0xC9, 0x1C }, 846ea4383bSHeiko Stuebner { 0xCA, 0x28 }, 856ea4383bSHeiko Stuebner { 0xCB, 0x75 }, 866ea4383bSHeiko Stuebner { 0xCC, 0x1A }, 876ea4383bSHeiko Stuebner { 0xCD, 0x19 }, 886ea4383bSHeiko Stuebner { 0xCE, 0x4D }, 896ea4383bSHeiko Stuebner { 0xCF, 0x22 }, 906ea4383bSHeiko Stuebner { 0xD0, 0x28 }, 916ea4383bSHeiko Stuebner { 0xD1, 0x54 }, 926ea4383bSHeiko Stuebner { 0xD2, 0x66 }, 936ea4383bSHeiko Stuebner { 0xD3, 0x39 }, 946ea4383bSHeiko Stuebner }; 956ea4383bSHeiko Stuebner 966ea4383bSHeiko Stuebner static const struct ltk050h3146w_cmd page3_cmds[] = { 976ea4383bSHeiko Stuebner { 0x01, 0x00 }, 986ea4383bSHeiko Stuebner { 0x02, 0x00 }, 996ea4383bSHeiko Stuebner { 0x03, 0x73 }, 1006ea4383bSHeiko Stuebner { 0x04, 0x00 }, 1016ea4383bSHeiko Stuebner { 0x05, 0x00 }, 1026ea4383bSHeiko Stuebner { 0x06, 0x0a }, 1036ea4383bSHeiko Stuebner { 0x07, 0x00 }, 1046ea4383bSHeiko Stuebner { 0x08, 0x00 }, 1056ea4383bSHeiko Stuebner { 0x09, 0x01 }, 1066ea4383bSHeiko Stuebner { 0x0a, 0x00 }, 1076ea4383bSHeiko Stuebner { 0x0b, 0x00 }, 1086ea4383bSHeiko Stuebner { 0x0c, 0x01 }, 1096ea4383bSHeiko Stuebner { 0x0d, 0x00 }, 1106ea4383bSHeiko Stuebner { 0x0e, 0x00 }, 1116ea4383bSHeiko Stuebner { 0x0f, 0x1d }, 1126ea4383bSHeiko Stuebner { 0x10, 0x1d }, 1136ea4383bSHeiko Stuebner { 0x11, 0x00 }, 1146ea4383bSHeiko Stuebner { 0x12, 0x00 }, 1156ea4383bSHeiko Stuebner { 0x13, 0x00 }, 1166ea4383bSHeiko Stuebner { 0x14, 0x00 }, 1176ea4383bSHeiko Stuebner { 0x15, 0x00 }, 1186ea4383bSHeiko Stuebner { 0x16, 0x00 }, 1196ea4383bSHeiko Stuebner { 0x17, 0x00 }, 1206ea4383bSHeiko Stuebner { 0x18, 0x00 }, 1216ea4383bSHeiko Stuebner { 0x19, 0x00 }, 1226ea4383bSHeiko Stuebner { 0x1a, 0x00 }, 1236ea4383bSHeiko Stuebner { 0x1b, 0x00 }, 1246ea4383bSHeiko Stuebner { 0x1c, 0x00 }, 1256ea4383bSHeiko Stuebner { 0x1d, 0x00 }, 1266ea4383bSHeiko Stuebner { 0x1e, 0x40 }, 1276ea4383bSHeiko Stuebner { 0x1f, 0x80 }, 1286ea4383bSHeiko Stuebner { 0x20, 0x06 }, 1296ea4383bSHeiko Stuebner { 0x21, 0x02 }, 1306ea4383bSHeiko Stuebner { 0x22, 0x00 }, 1316ea4383bSHeiko Stuebner { 0x23, 0x00 }, 1326ea4383bSHeiko Stuebner { 0x24, 0x00 }, 1336ea4383bSHeiko Stuebner { 0x25, 0x00 }, 1346ea4383bSHeiko Stuebner { 0x26, 0x00 }, 1356ea4383bSHeiko Stuebner { 0x27, 0x00 }, 1366ea4383bSHeiko Stuebner { 0x28, 0x33 }, 1376ea4383bSHeiko Stuebner { 0x29, 0x03 }, 1386ea4383bSHeiko Stuebner { 0x2a, 0x00 }, 1396ea4383bSHeiko Stuebner { 0x2b, 0x00 }, 1406ea4383bSHeiko Stuebner { 0x2c, 0x00 }, 1416ea4383bSHeiko Stuebner { 0x2d, 0x00 }, 1426ea4383bSHeiko Stuebner { 0x2e, 0x00 }, 1436ea4383bSHeiko Stuebner { 0x2f, 0x00 }, 1446ea4383bSHeiko Stuebner { 0x30, 0x00 }, 1456ea4383bSHeiko Stuebner { 0x31, 0x00 }, 1466ea4383bSHeiko Stuebner { 0x32, 0x00 }, 1476ea4383bSHeiko Stuebner { 0x33, 0x00 }, 1486ea4383bSHeiko Stuebner { 0x34, 0x04 }, 1496ea4383bSHeiko Stuebner { 0x35, 0x00 }, 1506ea4383bSHeiko Stuebner { 0x36, 0x00 }, 1516ea4383bSHeiko Stuebner { 0x37, 0x00 }, 1526ea4383bSHeiko Stuebner { 0x38, 0x3C }, 1536ea4383bSHeiko Stuebner { 0x39, 0x35 }, 1546ea4383bSHeiko Stuebner { 0x3A, 0x01 }, 1556ea4383bSHeiko Stuebner { 0x3B, 0x40 }, 1566ea4383bSHeiko Stuebner { 0x3C, 0x00 }, 1576ea4383bSHeiko Stuebner { 0x3D, 0x01 }, 1586ea4383bSHeiko Stuebner { 0x3E, 0x00 }, 1596ea4383bSHeiko Stuebner { 0x3F, 0x00 }, 1606ea4383bSHeiko Stuebner { 0x40, 0x00 }, 1616ea4383bSHeiko Stuebner { 0x41, 0x88 }, 1626ea4383bSHeiko Stuebner { 0x42, 0x00 }, 1636ea4383bSHeiko Stuebner { 0x43, 0x00 }, 1646ea4383bSHeiko Stuebner { 0x44, 0x1F }, 1656ea4383bSHeiko Stuebner { 0x50, 0x01 }, 1666ea4383bSHeiko Stuebner { 0x51, 0x23 }, 1676ea4383bSHeiko Stuebner { 0x52, 0x45 }, 1686ea4383bSHeiko Stuebner { 0x53, 0x67 }, 1696ea4383bSHeiko Stuebner { 0x54, 0x89 }, 1706ea4383bSHeiko Stuebner { 0x55, 0xab }, 1716ea4383bSHeiko Stuebner { 0x56, 0x01 }, 1726ea4383bSHeiko Stuebner { 0x57, 0x23 }, 1736ea4383bSHeiko Stuebner { 0x58, 0x45 }, 1746ea4383bSHeiko Stuebner { 0x59, 0x67 }, 1756ea4383bSHeiko Stuebner { 0x5a, 0x89 }, 1766ea4383bSHeiko Stuebner { 0x5b, 0xab }, 1776ea4383bSHeiko Stuebner { 0x5c, 0xcd }, 1786ea4383bSHeiko Stuebner { 0x5d, 0xef }, 1796ea4383bSHeiko Stuebner { 0x5e, 0x11 }, 1806ea4383bSHeiko Stuebner { 0x5f, 0x01 }, 1816ea4383bSHeiko Stuebner { 0x60, 0x00 }, 1826ea4383bSHeiko Stuebner { 0x61, 0x15 }, 1836ea4383bSHeiko Stuebner { 0x62, 0x14 }, 1846ea4383bSHeiko Stuebner { 0x63, 0x0E }, 1856ea4383bSHeiko Stuebner { 0x64, 0x0F }, 1866ea4383bSHeiko Stuebner { 0x65, 0x0C }, 1876ea4383bSHeiko Stuebner { 0x66, 0x0D }, 1886ea4383bSHeiko Stuebner { 0x67, 0x06 }, 1896ea4383bSHeiko Stuebner { 0x68, 0x02 }, 1906ea4383bSHeiko Stuebner { 0x69, 0x07 }, 1916ea4383bSHeiko Stuebner { 0x6a, 0x02 }, 1926ea4383bSHeiko Stuebner { 0x6b, 0x02 }, 1936ea4383bSHeiko Stuebner { 0x6c, 0x02 }, 1946ea4383bSHeiko Stuebner { 0x6d, 0x02 }, 1956ea4383bSHeiko Stuebner { 0x6e, 0x02 }, 1966ea4383bSHeiko Stuebner { 0x6f, 0x02 }, 1976ea4383bSHeiko Stuebner { 0x70, 0x02 }, 1986ea4383bSHeiko Stuebner { 0x71, 0x02 }, 1996ea4383bSHeiko Stuebner { 0x72, 0x02 }, 2006ea4383bSHeiko Stuebner { 0x73, 0x02 }, 2016ea4383bSHeiko Stuebner { 0x74, 0x02 }, 2026ea4383bSHeiko Stuebner { 0x75, 0x01 }, 2036ea4383bSHeiko Stuebner { 0x76, 0x00 }, 2046ea4383bSHeiko Stuebner { 0x77, 0x14 }, 2056ea4383bSHeiko Stuebner { 0x78, 0x15 }, 2066ea4383bSHeiko Stuebner { 0x79, 0x0E }, 2076ea4383bSHeiko Stuebner { 0x7a, 0x0F }, 2086ea4383bSHeiko Stuebner { 0x7b, 0x0C }, 2096ea4383bSHeiko Stuebner { 0x7c, 0x0D }, 2106ea4383bSHeiko Stuebner { 0x7d, 0x06 }, 2116ea4383bSHeiko Stuebner { 0x7e, 0x02 }, 2126ea4383bSHeiko Stuebner { 0x7f, 0x07 }, 2136ea4383bSHeiko Stuebner { 0x80, 0x02 }, 2146ea4383bSHeiko Stuebner { 0x81, 0x02 }, 2156ea4383bSHeiko Stuebner { 0x82, 0x02 }, 2166ea4383bSHeiko Stuebner { 0x83, 0x02 }, 2176ea4383bSHeiko Stuebner { 0x84, 0x02 }, 2186ea4383bSHeiko Stuebner { 0x85, 0x02 }, 2196ea4383bSHeiko Stuebner { 0x86, 0x02 }, 2206ea4383bSHeiko Stuebner { 0x87, 0x02 }, 2216ea4383bSHeiko Stuebner { 0x88, 0x02 }, 2226ea4383bSHeiko Stuebner { 0x89, 0x02 }, 2236ea4383bSHeiko Stuebner { 0x8A, 0x02 }, 2246ea4383bSHeiko Stuebner }; 2256ea4383bSHeiko Stuebner 2266ea4383bSHeiko Stuebner static const struct ltk050h3146w_cmd page4_cmds[] = { 2276ea4383bSHeiko Stuebner { 0x70, 0x00 }, 2286ea4383bSHeiko Stuebner { 0x71, 0x00 }, 2296ea4383bSHeiko Stuebner { 0x82, 0x0F }, /* VGH_MOD clamp level=15v */ 2306ea4383bSHeiko Stuebner { 0x84, 0x0F }, /* VGH clamp level 15V */ 2316ea4383bSHeiko Stuebner { 0x85, 0x0D }, /* VGL clamp level (-10V) */ 2326ea4383bSHeiko Stuebner { 0x32, 0xAC }, 2336ea4383bSHeiko Stuebner { 0x8C, 0x80 }, 2346ea4383bSHeiko Stuebner { 0x3C, 0xF5 }, 2356ea4383bSHeiko Stuebner { 0xB5, 0x07 }, /* GAMMA OP */ 2366ea4383bSHeiko Stuebner { 0x31, 0x45 }, /* SOURCE OP */ 2376ea4383bSHeiko Stuebner { 0x3A, 0x24 }, /* PS_EN OFF */ 2386ea4383bSHeiko Stuebner { 0x88, 0x33 }, /* LVD */ 2396ea4383bSHeiko Stuebner }; 2406ea4383bSHeiko Stuebner 2416ea4383bSHeiko Stuebner static inline 2426ea4383bSHeiko Stuebner struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel) 2436ea4383bSHeiko Stuebner { 2446ea4383bSHeiko Stuebner return container_of(panel, struct ltk050h3146w, panel); 2456ea4383bSHeiko Stuebner } 2466ea4383bSHeiko Stuebner 2476ea4383bSHeiko Stuebner #define dsi_dcs_write_seq(dsi, cmd, seq...) do { \ 2481a5c4fe9SEmil Velikov static const u8 b[] = { cmd, seq }; \ 2496ea4383bSHeiko Stuebner int ret; \ 2501a5c4fe9SEmil Velikov ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ 2516ea4383bSHeiko Stuebner if (ret < 0) \ 2526ea4383bSHeiko Stuebner return ret; \ 2536ea4383bSHeiko Stuebner } while (0) 2546ea4383bSHeiko Stuebner 2556ea4383bSHeiko Stuebner static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx) 2566ea4383bSHeiko Stuebner { 2576ea4383bSHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 2586ea4383bSHeiko Stuebner int ret; 2596ea4383bSHeiko Stuebner 2606ea4383bSHeiko Stuebner /* 2616ea4383bSHeiko Stuebner * Init sequence was supplied by the panel vendor without much 2626ea4383bSHeiko Stuebner * documentation. 2636ea4383bSHeiko Stuebner */ 2646ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8); 2656ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06, 2666ea4383bSHeiko Stuebner 0x01); 2676ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5); 2686ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5); 2696ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00); 2706ea4383bSHeiko Stuebner 2716ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07); 2726ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f, 2736ea4383bSHeiko Stuebner 0x28, 0x04, 0xcc, 0xcc, 0xcc); 2746ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04); 2756ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2); 2766ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03); 2776ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12); 2786ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80, 2796ea4383bSHeiko Stuebner 0x80); 2806ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f, 2816ea4383bSHeiko Stuebner 0x16, 0x00, 0x00); 2826ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50, 2836ea4383bSHeiko Stuebner 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f, 2846ea4383bSHeiko Stuebner 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67, 2856ea4383bSHeiko Stuebner 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55, 2866ea4383bSHeiko Stuebner 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08); 2876ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a, 2886ea4383bSHeiko Stuebner 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f, 2896ea4383bSHeiko Stuebner 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 2906ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b, 2916ea4383bSHeiko Stuebner 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f, 2926ea4383bSHeiko Stuebner 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 2936ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05, 2946ea4383bSHeiko Stuebner 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f, 2956ea4383bSHeiko Stuebner 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 2966ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04, 2976ea4383bSHeiko Stuebner 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f, 2986ea4383bSHeiko Stuebner 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 2996ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20, 3006ea4383bSHeiko Stuebner 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03, 3016ea4383bSHeiko Stuebner 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08); 3026ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00, 3036ea4383bSHeiko Stuebner 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05, 3046ea4383bSHeiko Stuebner 0x21, 0x00, 0x60); 3056ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00); 3066ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xde, 0x02); 3076ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c); 3086ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04); 3096ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xc1, 0x11); 3106ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37); 3116ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84); 3126ea4383bSHeiko Stuebner dsi_dcs_write_seq(dsi, 0xde, 0x00); 3136ea4383bSHeiko Stuebner 3146ea4383bSHeiko Stuebner ret = mipi_dsi_dcs_set_tear_on(dsi, 1); 3156ea4383bSHeiko Stuebner if (ret < 0) { 316b75efff5SSam Ravnborg dev_err(ctx->dev, "failed to set tear on: %d\n", ret); 3176ea4383bSHeiko Stuebner return ret; 3186ea4383bSHeiko Stuebner } 3196ea4383bSHeiko Stuebner 3206ea4383bSHeiko Stuebner msleep(60); 3216ea4383bSHeiko Stuebner 3226ea4383bSHeiko Stuebner return 0; 3236ea4383bSHeiko Stuebner } 3246ea4383bSHeiko Stuebner 3256ea4383bSHeiko Stuebner static const struct drm_display_mode ltk050h3146w_mode = { 3266ea4383bSHeiko Stuebner .hdisplay = 720, 3276ea4383bSHeiko Stuebner .hsync_start = 720 + 42, 3286ea4383bSHeiko Stuebner .hsync_end = 720 + 42 + 8, 3296ea4383bSHeiko Stuebner .htotal = 720 + 42 + 8 + 42, 3306ea4383bSHeiko Stuebner .vdisplay = 1280, 3316ea4383bSHeiko Stuebner .vsync_start = 1280 + 12, 3326ea4383bSHeiko Stuebner .vsync_end = 1280 + 12 + 4, 3336ea4383bSHeiko Stuebner .vtotal = 1280 + 12 + 4 + 18, 3346ea4383bSHeiko Stuebner .clock = 64018, 3356ea4383bSHeiko Stuebner .width_mm = 62, 3366ea4383bSHeiko Stuebner .height_mm = 110, 3376ea4383bSHeiko Stuebner }; 3386ea4383bSHeiko Stuebner 3396ea4383bSHeiko Stuebner static const struct ltk050h3146w_desc ltk050h3146w_data = { 3406ea4383bSHeiko Stuebner .mode = <k050h3146w_mode, 3416ea4383bSHeiko Stuebner .init = ltk050h3146w_init_sequence, 3426ea4383bSHeiko Stuebner }; 3436ea4383bSHeiko Stuebner 3446ea4383bSHeiko Stuebner static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page) 3456ea4383bSHeiko Stuebner { 3466ea4383bSHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 3476ea4383bSHeiko Stuebner u8 d[3] = { 0x98, 0x81, page }; 3486ea4383bSHeiko Stuebner 3496ea4383bSHeiko Stuebner return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d)); 3506ea4383bSHeiko Stuebner } 3516ea4383bSHeiko Stuebner 3526ea4383bSHeiko Stuebner static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page, 3536ea4383bSHeiko Stuebner const struct ltk050h3146w_cmd *cmds, 3546ea4383bSHeiko Stuebner int num) 3556ea4383bSHeiko Stuebner { 3566ea4383bSHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 3576ea4383bSHeiko Stuebner int i, ret; 3586ea4383bSHeiko Stuebner 3596ea4383bSHeiko Stuebner ret = ltk050h3146w_a2_select_page(ctx, page); 3606ea4383bSHeiko Stuebner if (ret < 0) { 361b75efff5SSam Ravnborg dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret); 3626ea4383bSHeiko Stuebner return ret; 3636ea4383bSHeiko Stuebner } 3646ea4383bSHeiko Stuebner 3656ea4383bSHeiko Stuebner for (i = 0; i < num; i++) { 3666ea4383bSHeiko Stuebner ret = mipi_dsi_generic_write(dsi, &cmds[i], 3676ea4383bSHeiko Stuebner sizeof(struct ltk050h3146w_cmd)); 3686ea4383bSHeiko Stuebner if (ret < 0) { 369b75efff5SSam Ravnborg dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret); 3706ea4383bSHeiko Stuebner return ret; 3716ea4383bSHeiko Stuebner } 3726ea4383bSHeiko Stuebner } 3736ea4383bSHeiko Stuebner 3746ea4383bSHeiko Stuebner return 0; 3756ea4383bSHeiko Stuebner } 3766ea4383bSHeiko Stuebner 3776ea4383bSHeiko Stuebner static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx) 3786ea4383bSHeiko Stuebner { 3796ea4383bSHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 3806ea4383bSHeiko Stuebner int ret; 3816ea4383bSHeiko Stuebner 3826ea4383bSHeiko Stuebner /* 3836ea4383bSHeiko Stuebner * Init sequence was supplied by the panel vendor without much 3846ea4383bSHeiko Stuebner * documentation. 3856ea4383bSHeiko Stuebner */ 3866ea4383bSHeiko Stuebner ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds, 3876ea4383bSHeiko Stuebner ARRAY_SIZE(page3_cmds)); 3886ea4383bSHeiko Stuebner if (ret < 0) 3896ea4383bSHeiko Stuebner return ret; 3906ea4383bSHeiko Stuebner 3916ea4383bSHeiko Stuebner ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds, 3926ea4383bSHeiko Stuebner ARRAY_SIZE(page4_cmds)); 3936ea4383bSHeiko Stuebner if (ret < 0) 3946ea4383bSHeiko Stuebner return ret; 3956ea4383bSHeiko Stuebner 3966ea4383bSHeiko Stuebner ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds, 3976ea4383bSHeiko Stuebner ARRAY_SIZE(page1_cmds)); 3986ea4383bSHeiko Stuebner if (ret < 0) 3996ea4383bSHeiko Stuebner return ret; 4006ea4383bSHeiko Stuebner 4016ea4383bSHeiko Stuebner ret = ltk050h3146w_a2_select_page(ctx, 0); 4026ea4383bSHeiko Stuebner if (ret < 0) { 403b75efff5SSam Ravnborg dev_err(ctx->dev, "failed to select page 0: %d\n", ret); 4046ea4383bSHeiko Stuebner return ret; 4056ea4383bSHeiko Stuebner } 4066ea4383bSHeiko Stuebner 4076ea4383bSHeiko Stuebner /* vendor code called this without param, where there should be one */ 4086ea4383bSHeiko Stuebner ret = mipi_dsi_dcs_set_tear_on(dsi, 0); 4096ea4383bSHeiko Stuebner if (ret < 0) { 410b75efff5SSam Ravnborg dev_err(ctx->dev, "failed to set tear on: %d\n", ret); 4116ea4383bSHeiko Stuebner return ret; 4126ea4383bSHeiko Stuebner } 4136ea4383bSHeiko Stuebner 4146ea4383bSHeiko Stuebner msleep(60); 4156ea4383bSHeiko Stuebner 4166ea4383bSHeiko Stuebner return 0; 4176ea4383bSHeiko Stuebner } 4186ea4383bSHeiko Stuebner 4196ea4383bSHeiko Stuebner static const struct drm_display_mode ltk050h3146w_a2_mode = { 4206ea4383bSHeiko Stuebner .hdisplay = 720, 4216ea4383bSHeiko Stuebner .hsync_start = 720 + 42, 4226ea4383bSHeiko Stuebner .hsync_end = 720 + 42 + 10, 4236ea4383bSHeiko Stuebner .htotal = 720 + 42 + 10 + 60, 4246ea4383bSHeiko Stuebner .vdisplay = 1280, 4256ea4383bSHeiko Stuebner .vsync_start = 1280 + 18, 4266ea4383bSHeiko Stuebner .vsync_end = 1280 + 18 + 4, 4276ea4383bSHeiko Stuebner .vtotal = 1280 + 18 + 4 + 12, 4286ea4383bSHeiko Stuebner .clock = 65595, 4296ea4383bSHeiko Stuebner .width_mm = 62, 4306ea4383bSHeiko Stuebner .height_mm = 110, 4316ea4383bSHeiko Stuebner }; 4326ea4383bSHeiko Stuebner 4336ea4383bSHeiko Stuebner static const struct ltk050h3146w_desc ltk050h3146w_a2_data = { 4346ea4383bSHeiko Stuebner .mode = <k050h3146w_a2_mode, 4356ea4383bSHeiko Stuebner .init = ltk050h3146w_a2_init_sequence, 4366ea4383bSHeiko Stuebner }; 4376ea4383bSHeiko Stuebner 4386ea4383bSHeiko Stuebner static int ltk050h3146w_unprepare(struct drm_panel *panel) 4396ea4383bSHeiko Stuebner { 4406ea4383bSHeiko Stuebner struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 4416ea4383bSHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 4426ea4383bSHeiko Stuebner int ret; 4436ea4383bSHeiko Stuebner 4446ea4383bSHeiko Stuebner if (!ctx->prepared) 4456ea4383bSHeiko Stuebner return 0; 4466ea4383bSHeiko Stuebner 4476ea4383bSHeiko Stuebner ret = mipi_dsi_dcs_set_display_off(dsi); 4486ea4383bSHeiko Stuebner if (ret < 0) { 449b75efff5SSam Ravnborg dev_err(ctx->dev, "failed to set display off: %d\n", ret); 4506ea4383bSHeiko Stuebner return ret; 4516ea4383bSHeiko Stuebner } 4526ea4383bSHeiko Stuebner 4536ea4383bSHeiko Stuebner mipi_dsi_dcs_enter_sleep_mode(dsi); 4546ea4383bSHeiko Stuebner if (ret < 0) { 455b75efff5SSam Ravnborg dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret); 4566ea4383bSHeiko Stuebner return ret; 4576ea4383bSHeiko Stuebner } 4586ea4383bSHeiko Stuebner 4596ea4383bSHeiko Stuebner regulator_disable(ctx->iovcc); 4606ea4383bSHeiko Stuebner regulator_disable(ctx->vci); 4616ea4383bSHeiko Stuebner 4626ea4383bSHeiko Stuebner ctx->prepared = false; 4636ea4383bSHeiko Stuebner 4646ea4383bSHeiko Stuebner return 0; 4656ea4383bSHeiko Stuebner } 4666ea4383bSHeiko Stuebner 4676ea4383bSHeiko Stuebner static int ltk050h3146w_prepare(struct drm_panel *panel) 4686ea4383bSHeiko Stuebner { 4696ea4383bSHeiko Stuebner struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 4706ea4383bSHeiko Stuebner struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 4716ea4383bSHeiko Stuebner int ret; 4726ea4383bSHeiko Stuebner 4736ea4383bSHeiko Stuebner if (ctx->prepared) 4746ea4383bSHeiko Stuebner return 0; 4756ea4383bSHeiko Stuebner 476b75efff5SSam Ravnborg dev_dbg(ctx->dev, "Resetting the panel\n"); 4776ea4383bSHeiko Stuebner ret = regulator_enable(ctx->vci); 4786ea4383bSHeiko Stuebner if (ret < 0) { 479b75efff5SSam Ravnborg dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret); 4806ea4383bSHeiko Stuebner return ret; 4816ea4383bSHeiko Stuebner } 4826ea4383bSHeiko Stuebner ret = regulator_enable(ctx->iovcc); 4836ea4383bSHeiko Stuebner if (ret < 0) { 484b75efff5SSam Ravnborg dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret); 4856ea4383bSHeiko Stuebner goto disable_vci; 4866ea4383bSHeiko Stuebner } 4876ea4383bSHeiko Stuebner 4886ea4383bSHeiko Stuebner gpiod_set_value_cansleep(ctx->reset_gpio, 1); 4896ea4383bSHeiko Stuebner usleep_range(5000, 6000); 4906ea4383bSHeiko Stuebner gpiod_set_value_cansleep(ctx->reset_gpio, 0); 4916ea4383bSHeiko Stuebner msleep(20); 4926ea4383bSHeiko Stuebner 4936ea4383bSHeiko Stuebner ret = ctx->panel_desc->init(ctx); 4946ea4383bSHeiko Stuebner if (ret < 0) { 495b75efff5SSam Ravnborg dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret); 4966ea4383bSHeiko Stuebner goto disable_iovcc; 4976ea4383bSHeiko Stuebner } 4986ea4383bSHeiko Stuebner 4996ea4383bSHeiko Stuebner ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 5006ea4383bSHeiko Stuebner if (ret < 0) { 501b75efff5SSam Ravnborg dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret); 5026ea4383bSHeiko Stuebner goto disable_iovcc; 5036ea4383bSHeiko Stuebner } 5046ea4383bSHeiko Stuebner 5056ea4383bSHeiko Stuebner /* T9: 120ms */ 5066ea4383bSHeiko Stuebner msleep(120); 5076ea4383bSHeiko Stuebner 5086ea4383bSHeiko Stuebner ret = mipi_dsi_dcs_set_display_on(dsi); 5096ea4383bSHeiko Stuebner if (ret < 0) { 510b75efff5SSam Ravnborg dev_err(ctx->dev, "Failed to set display on: %d\n", ret); 5116ea4383bSHeiko Stuebner goto disable_iovcc; 5126ea4383bSHeiko Stuebner } 5136ea4383bSHeiko Stuebner 5146ea4383bSHeiko Stuebner msleep(50); 5156ea4383bSHeiko Stuebner 5166ea4383bSHeiko Stuebner ctx->prepared = true; 5176ea4383bSHeiko Stuebner 5186ea4383bSHeiko Stuebner return 0; 5196ea4383bSHeiko Stuebner 5206ea4383bSHeiko Stuebner disable_iovcc: 5216ea4383bSHeiko Stuebner regulator_disable(ctx->iovcc); 5226ea4383bSHeiko Stuebner disable_vci: 5236ea4383bSHeiko Stuebner regulator_disable(ctx->vci); 5246ea4383bSHeiko Stuebner return ret; 5256ea4383bSHeiko Stuebner } 5266ea4383bSHeiko Stuebner 5276ea4383bSHeiko Stuebner static int ltk050h3146w_get_modes(struct drm_panel *panel, 5286ea4383bSHeiko Stuebner struct drm_connector *connector) 5296ea4383bSHeiko Stuebner { 5306ea4383bSHeiko Stuebner struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 5316ea4383bSHeiko Stuebner struct drm_display_mode *mode; 5326ea4383bSHeiko Stuebner 5336ea4383bSHeiko Stuebner mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode); 5346ea4383bSHeiko Stuebner if (!mode) 5356ea4383bSHeiko Stuebner return -ENOMEM; 5366ea4383bSHeiko Stuebner 5376ea4383bSHeiko Stuebner drm_mode_set_name(mode); 5386ea4383bSHeiko Stuebner 5396ea4383bSHeiko Stuebner mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 5406ea4383bSHeiko Stuebner connector->display_info.width_mm = mode->width_mm; 5416ea4383bSHeiko Stuebner connector->display_info.height_mm = mode->height_mm; 5426ea4383bSHeiko Stuebner drm_mode_probed_add(connector, mode); 5436ea4383bSHeiko Stuebner 5446ea4383bSHeiko Stuebner return 1; 5456ea4383bSHeiko Stuebner } 5466ea4383bSHeiko Stuebner 5476ea4383bSHeiko Stuebner static const struct drm_panel_funcs ltk050h3146w_funcs = { 5486ea4383bSHeiko Stuebner .unprepare = ltk050h3146w_unprepare, 5496ea4383bSHeiko Stuebner .prepare = ltk050h3146w_prepare, 5506ea4383bSHeiko Stuebner .get_modes = ltk050h3146w_get_modes, 5516ea4383bSHeiko Stuebner }; 5526ea4383bSHeiko Stuebner 5536ea4383bSHeiko Stuebner static int ltk050h3146w_probe(struct mipi_dsi_device *dsi) 5546ea4383bSHeiko Stuebner { 5556ea4383bSHeiko Stuebner struct device *dev = &dsi->dev; 5566ea4383bSHeiko Stuebner struct ltk050h3146w *ctx; 5576ea4383bSHeiko Stuebner int ret; 5586ea4383bSHeiko Stuebner 5596ea4383bSHeiko Stuebner ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 5606ea4383bSHeiko Stuebner if (!ctx) 5616ea4383bSHeiko Stuebner return -ENOMEM; 5626ea4383bSHeiko Stuebner 5636ea4383bSHeiko Stuebner ctx->panel_desc = of_device_get_match_data(dev); 5646ea4383bSHeiko Stuebner if (!ctx->panel_desc) 5656ea4383bSHeiko Stuebner return -EINVAL; 5666ea4383bSHeiko Stuebner 5676ea4383bSHeiko Stuebner ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 5686ea4383bSHeiko Stuebner if (IS_ERR(ctx->reset_gpio)) { 569b75efff5SSam Ravnborg dev_err(dev, "cannot get reset gpio\n"); 5706ea4383bSHeiko Stuebner return PTR_ERR(ctx->reset_gpio); 5716ea4383bSHeiko Stuebner } 5726ea4383bSHeiko Stuebner 5736ea4383bSHeiko Stuebner ctx->vci = devm_regulator_get(dev, "vci"); 5746ea4383bSHeiko Stuebner if (IS_ERR(ctx->vci)) { 5756ea4383bSHeiko Stuebner ret = PTR_ERR(ctx->vci); 5766ea4383bSHeiko Stuebner if (ret != -EPROBE_DEFER) 577b75efff5SSam Ravnborg dev_err(dev, "Failed to request vci regulator: %d\n", ret); 5786ea4383bSHeiko Stuebner return ret; 5796ea4383bSHeiko Stuebner } 5806ea4383bSHeiko Stuebner 5816ea4383bSHeiko Stuebner ctx->iovcc = devm_regulator_get(dev, "iovcc"); 5826ea4383bSHeiko Stuebner if (IS_ERR(ctx->iovcc)) { 5836ea4383bSHeiko Stuebner ret = PTR_ERR(ctx->iovcc); 5846ea4383bSHeiko Stuebner if (ret != -EPROBE_DEFER) 585b75efff5SSam Ravnborg dev_err(dev, "Failed to request iovcc regulator: %d\n", ret); 5866ea4383bSHeiko Stuebner return ret; 5876ea4383bSHeiko Stuebner } 5886ea4383bSHeiko Stuebner 5896ea4383bSHeiko Stuebner mipi_dsi_set_drvdata(dsi, ctx); 5906ea4383bSHeiko Stuebner 5916ea4383bSHeiko Stuebner ctx->dev = dev; 5926ea4383bSHeiko Stuebner 5936ea4383bSHeiko Stuebner dsi->lanes = 4; 5946ea4383bSHeiko Stuebner dsi->format = MIPI_DSI_FMT_RGB888; 5956ea4383bSHeiko Stuebner dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 596*0f3b68b6SNicolas Boichat MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; 5976ea4383bSHeiko Stuebner 5986ea4383bSHeiko Stuebner drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs, 5996ea4383bSHeiko Stuebner DRM_MODE_CONNECTOR_DSI); 6006ea4383bSHeiko Stuebner 6016ea4383bSHeiko Stuebner ret = drm_panel_of_backlight(&ctx->panel); 6026ea4383bSHeiko Stuebner if (ret) 6036ea4383bSHeiko Stuebner return ret; 6046ea4383bSHeiko Stuebner 6056ea4383bSHeiko Stuebner drm_panel_add(&ctx->panel); 6066ea4383bSHeiko Stuebner 6076ea4383bSHeiko Stuebner ret = mipi_dsi_attach(dsi); 6086ea4383bSHeiko Stuebner if (ret < 0) { 609b75efff5SSam Ravnborg dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); 6106ea4383bSHeiko Stuebner drm_panel_remove(&ctx->panel); 6116ea4383bSHeiko Stuebner return ret; 6126ea4383bSHeiko Stuebner } 6136ea4383bSHeiko Stuebner 6146ea4383bSHeiko Stuebner return 0; 6156ea4383bSHeiko Stuebner } 6166ea4383bSHeiko Stuebner 6176ea4383bSHeiko Stuebner static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi) 6186ea4383bSHeiko Stuebner { 6196ea4383bSHeiko Stuebner struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); 6206ea4383bSHeiko Stuebner int ret; 6216ea4383bSHeiko Stuebner 6226ea4383bSHeiko Stuebner ret = drm_panel_unprepare(&ctx->panel); 6236ea4383bSHeiko Stuebner if (ret < 0) 624b75efff5SSam Ravnborg dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret); 6256ea4383bSHeiko Stuebner 6266ea4383bSHeiko Stuebner ret = drm_panel_disable(&ctx->panel); 6276ea4383bSHeiko Stuebner if (ret < 0) 628b75efff5SSam Ravnborg dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); 6296ea4383bSHeiko Stuebner } 6306ea4383bSHeiko Stuebner 6316ea4383bSHeiko Stuebner static int ltk050h3146w_remove(struct mipi_dsi_device *dsi) 6326ea4383bSHeiko Stuebner { 6336ea4383bSHeiko Stuebner struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); 6346ea4383bSHeiko Stuebner int ret; 6356ea4383bSHeiko Stuebner 6366ea4383bSHeiko Stuebner ltk050h3146w_shutdown(dsi); 6376ea4383bSHeiko Stuebner 6386ea4383bSHeiko Stuebner ret = mipi_dsi_detach(dsi); 6396ea4383bSHeiko Stuebner if (ret < 0) 640b75efff5SSam Ravnborg dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 6416ea4383bSHeiko Stuebner 6426ea4383bSHeiko Stuebner drm_panel_remove(&ctx->panel); 6436ea4383bSHeiko Stuebner 6446ea4383bSHeiko Stuebner return 0; 6456ea4383bSHeiko Stuebner } 6466ea4383bSHeiko Stuebner 6476ea4383bSHeiko Stuebner static const struct of_device_id ltk050h3146w_of_match[] = { 6486ea4383bSHeiko Stuebner { 6496ea4383bSHeiko Stuebner .compatible = "leadtek,ltk050h3146w", 6506ea4383bSHeiko Stuebner .data = <k050h3146w_data, 6516ea4383bSHeiko Stuebner }, 6526ea4383bSHeiko Stuebner { 6536ea4383bSHeiko Stuebner .compatible = "leadtek,ltk050h3146w-a2", 6546ea4383bSHeiko Stuebner .data = <k050h3146w_a2_data, 6556ea4383bSHeiko Stuebner }, 6566ea4383bSHeiko Stuebner { /* sentinel */ } 6576ea4383bSHeiko Stuebner }; 6586ea4383bSHeiko Stuebner MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match); 6596ea4383bSHeiko Stuebner 6606ea4383bSHeiko Stuebner static struct mipi_dsi_driver ltk050h3146w_driver = { 6616ea4383bSHeiko Stuebner .driver = { 6626ea4383bSHeiko Stuebner .name = "panel-leadtek-ltk050h3146w", 6636ea4383bSHeiko Stuebner .of_match_table = ltk050h3146w_of_match, 6646ea4383bSHeiko Stuebner }, 6656ea4383bSHeiko Stuebner .probe = ltk050h3146w_probe, 6666ea4383bSHeiko Stuebner .remove = ltk050h3146w_remove, 6676ea4383bSHeiko Stuebner .shutdown = ltk050h3146w_shutdown, 6686ea4383bSHeiko Stuebner }; 6696ea4383bSHeiko Stuebner module_mipi_dsi_driver(ltk050h3146w_driver); 6706ea4383bSHeiko Stuebner 6716ea4383bSHeiko Stuebner MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 6726ea4383bSHeiko Stuebner MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel"); 6736ea4383bSHeiko Stuebner MODULE_LICENSE("GPL v2"); 674