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 = &ltk050h3146w_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 = &ltk050h3146w_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, &ltk050h3146w_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 = &ltk050h3146w_data,
6516ea4383bSHeiko Stuebner 	},
6526ea4383bSHeiko Stuebner 	{
6536ea4383bSHeiko Stuebner 		.compatible = "leadtek,ltk050h3146w-a2",
6546ea4383bSHeiko Stuebner 		.data = &ltk050h3146w_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