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