18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 88c2ecf20Sopenharmony_ci#include <linux/media-bus-format.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/of_device.h> 128c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <video/display_timing.h> 158c2ecf20Sopenharmony_ci#include <video/mipi_display.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_modes.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct ltk050h3146w_cmd { 228c2ecf20Sopenharmony_ci char cmd; 238c2ecf20Sopenharmony_ci char data; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct ltk050h3146w; 278c2ecf20Sopenharmony_cistruct ltk050h3146w_desc { 288c2ecf20Sopenharmony_ci const struct drm_display_mode *mode; 298c2ecf20Sopenharmony_ci int (*init)(struct ltk050h3146w *ctx); 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct ltk050h3146w { 338c2ecf20Sopenharmony_ci struct device *dev; 348c2ecf20Sopenharmony_ci struct drm_panel panel; 358c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 368c2ecf20Sopenharmony_ci struct regulator *vci; 378c2ecf20Sopenharmony_ci struct regulator *iovcc; 388c2ecf20Sopenharmony_ci const struct ltk050h3146w_desc *panel_desc; 398c2ecf20Sopenharmony_ci bool prepared; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const struct ltk050h3146w_cmd page1_cmds[] = { 438c2ecf20Sopenharmony_ci { 0x22, 0x0A }, /* BGR SS GS */ 448c2ecf20Sopenharmony_ci { 0x31, 0x00 }, /* column inversion */ 458c2ecf20Sopenharmony_ci { 0x53, 0xA2 }, /* VCOM1 */ 468c2ecf20Sopenharmony_ci { 0x55, 0xA2 }, /* VCOM2 */ 478c2ecf20Sopenharmony_ci { 0x50, 0x81 }, /* VREG1OUT=5V */ 488c2ecf20Sopenharmony_ci { 0x51, 0x85 }, /* VREG2OUT=-5V */ 498c2ecf20Sopenharmony_ci { 0x62, 0x0D }, /* EQT Time setting */ 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * The vendor init selected page 1 here _again_ 528c2ecf20Sopenharmony_ci * Is this supposed to be page 2? 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci { 0xA0, 0x00 }, 558c2ecf20Sopenharmony_ci { 0xA1, 0x1A }, 568c2ecf20Sopenharmony_ci { 0xA2, 0x28 }, 578c2ecf20Sopenharmony_ci { 0xA3, 0x13 }, 588c2ecf20Sopenharmony_ci { 0xA4, 0x16 }, 598c2ecf20Sopenharmony_ci { 0xA5, 0x29 }, 608c2ecf20Sopenharmony_ci { 0xA6, 0x1D }, 618c2ecf20Sopenharmony_ci { 0xA7, 0x1E }, 628c2ecf20Sopenharmony_ci { 0xA8, 0x84 }, 638c2ecf20Sopenharmony_ci { 0xA9, 0x1C }, 648c2ecf20Sopenharmony_ci { 0xAA, 0x28 }, 658c2ecf20Sopenharmony_ci { 0xAB, 0x75 }, 668c2ecf20Sopenharmony_ci { 0xAC, 0x1A }, 678c2ecf20Sopenharmony_ci { 0xAD, 0x19 }, 688c2ecf20Sopenharmony_ci { 0xAE, 0x4D }, 698c2ecf20Sopenharmony_ci { 0xAF, 0x22 }, 708c2ecf20Sopenharmony_ci { 0xB0, 0x28 }, 718c2ecf20Sopenharmony_ci { 0xB1, 0x54 }, 728c2ecf20Sopenharmony_ci { 0xB2, 0x66 }, 738c2ecf20Sopenharmony_ci { 0xB3, 0x39 }, 748c2ecf20Sopenharmony_ci { 0xC0, 0x00 }, 758c2ecf20Sopenharmony_ci { 0xC1, 0x1A }, 768c2ecf20Sopenharmony_ci { 0xC2, 0x28 }, 778c2ecf20Sopenharmony_ci { 0xC3, 0x13 }, 788c2ecf20Sopenharmony_ci { 0xC4, 0x16 }, 798c2ecf20Sopenharmony_ci { 0xC5, 0x29 }, 808c2ecf20Sopenharmony_ci { 0xC6, 0x1D }, 818c2ecf20Sopenharmony_ci { 0xC7, 0x1E }, 828c2ecf20Sopenharmony_ci { 0xC8, 0x84 }, 838c2ecf20Sopenharmony_ci { 0xC9, 0x1C }, 848c2ecf20Sopenharmony_ci { 0xCA, 0x28 }, 858c2ecf20Sopenharmony_ci { 0xCB, 0x75 }, 868c2ecf20Sopenharmony_ci { 0xCC, 0x1A }, 878c2ecf20Sopenharmony_ci { 0xCD, 0x19 }, 888c2ecf20Sopenharmony_ci { 0xCE, 0x4D }, 898c2ecf20Sopenharmony_ci { 0xCF, 0x22 }, 908c2ecf20Sopenharmony_ci { 0xD0, 0x28 }, 918c2ecf20Sopenharmony_ci { 0xD1, 0x54 }, 928c2ecf20Sopenharmony_ci { 0xD2, 0x66 }, 938c2ecf20Sopenharmony_ci { 0xD3, 0x39 }, 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic const struct ltk050h3146w_cmd page3_cmds[] = { 978c2ecf20Sopenharmony_ci { 0x01, 0x00 }, 988c2ecf20Sopenharmony_ci { 0x02, 0x00 }, 998c2ecf20Sopenharmony_ci { 0x03, 0x73 }, 1008c2ecf20Sopenharmony_ci { 0x04, 0x00 }, 1018c2ecf20Sopenharmony_ci { 0x05, 0x00 }, 1028c2ecf20Sopenharmony_ci { 0x06, 0x0a }, 1038c2ecf20Sopenharmony_ci { 0x07, 0x00 }, 1048c2ecf20Sopenharmony_ci { 0x08, 0x00 }, 1058c2ecf20Sopenharmony_ci { 0x09, 0x01 }, 1068c2ecf20Sopenharmony_ci { 0x0a, 0x00 }, 1078c2ecf20Sopenharmony_ci { 0x0b, 0x00 }, 1088c2ecf20Sopenharmony_ci { 0x0c, 0x01 }, 1098c2ecf20Sopenharmony_ci { 0x0d, 0x00 }, 1108c2ecf20Sopenharmony_ci { 0x0e, 0x00 }, 1118c2ecf20Sopenharmony_ci { 0x0f, 0x1d }, 1128c2ecf20Sopenharmony_ci { 0x10, 0x1d }, 1138c2ecf20Sopenharmony_ci { 0x11, 0x00 }, 1148c2ecf20Sopenharmony_ci { 0x12, 0x00 }, 1158c2ecf20Sopenharmony_ci { 0x13, 0x00 }, 1168c2ecf20Sopenharmony_ci { 0x14, 0x00 }, 1178c2ecf20Sopenharmony_ci { 0x15, 0x00 }, 1188c2ecf20Sopenharmony_ci { 0x16, 0x00 }, 1198c2ecf20Sopenharmony_ci { 0x17, 0x00 }, 1208c2ecf20Sopenharmony_ci { 0x18, 0x00 }, 1218c2ecf20Sopenharmony_ci { 0x19, 0x00 }, 1228c2ecf20Sopenharmony_ci { 0x1a, 0x00 }, 1238c2ecf20Sopenharmony_ci { 0x1b, 0x00 }, 1248c2ecf20Sopenharmony_ci { 0x1c, 0x00 }, 1258c2ecf20Sopenharmony_ci { 0x1d, 0x00 }, 1268c2ecf20Sopenharmony_ci { 0x1e, 0x40 }, 1278c2ecf20Sopenharmony_ci { 0x1f, 0x80 }, 1288c2ecf20Sopenharmony_ci { 0x20, 0x06 }, 1298c2ecf20Sopenharmony_ci { 0x21, 0x02 }, 1308c2ecf20Sopenharmony_ci { 0x22, 0x00 }, 1318c2ecf20Sopenharmony_ci { 0x23, 0x00 }, 1328c2ecf20Sopenharmony_ci { 0x24, 0x00 }, 1338c2ecf20Sopenharmony_ci { 0x25, 0x00 }, 1348c2ecf20Sopenharmony_ci { 0x26, 0x00 }, 1358c2ecf20Sopenharmony_ci { 0x27, 0x00 }, 1368c2ecf20Sopenharmony_ci { 0x28, 0x33 }, 1378c2ecf20Sopenharmony_ci { 0x29, 0x03 }, 1388c2ecf20Sopenharmony_ci { 0x2a, 0x00 }, 1398c2ecf20Sopenharmony_ci { 0x2b, 0x00 }, 1408c2ecf20Sopenharmony_ci { 0x2c, 0x00 }, 1418c2ecf20Sopenharmony_ci { 0x2d, 0x00 }, 1428c2ecf20Sopenharmony_ci { 0x2e, 0x00 }, 1438c2ecf20Sopenharmony_ci { 0x2f, 0x00 }, 1448c2ecf20Sopenharmony_ci { 0x30, 0x00 }, 1458c2ecf20Sopenharmony_ci { 0x31, 0x00 }, 1468c2ecf20Sopenharmony_ci { 0x32, 0x00 }, 1478c2ecf20Sopenharmony_ci { 0x33, 0x00 }, 1488c2ecf20Sopenharmony_ci { 0x34, 0x04 }, 1498c2ecf20Sopenharmony_ci { 0x35, 0x00 }, 1508c2ecf20Sopenharmony_ci { 0x36, 0x00 }, 1518c2ecf20Sopenharmony_ci { 0x37, 0x00 }, 1528c2ecf20Sopenharmony_ci { 0x38, 0x3C }, 1538c2ecf20Sopenharmony_ci { 0x39, 0x35 }, 1548c2ecf20Sopenharmony_ci { 0x3A, 0x01 }, 1558c2ecf20Sopenharmony_ci { 0x3B, 0x40 }, 1568c2ecf20Sopenharmony_ci { 0x3C, 0x00 }, 1578c2ecf20Sopenharmony_ci { 0x3D, 0x01 }, 1588c2ecf20Sopenharmony_ci { 0x3E, 0x00 }, 1598c2ecf20Sopenharmony_ci { 0x3F, 0x00 }, 1608c2ecf20Sopenharmony_ci { 0x40, 0x00 }, 1618c2ecf20Sopenharmony_ci { 0x41, 0x88 }, 1628c2ecf20Sopenharmony_ci { 0x42, 0x00 }, 1638c2ecf20Sopenharmony_ci { 0x43, 0x00 }, 1648c2ecf20Sopenharmony_ci { 0x44, 0x1F }, 1658c2ecf20Sopenharmony_ci { 0x50, 0x01 }, 1668c2ecf20Sopenharmony_ci { 0x51, 0x23 }, 1678c2ecf20Sopenharmony_ci { 0x52, 0x45 }, 1688c2ecf20Sopenharmony_ci { 0x53, 0x67 }, 1698c2ecf20Sopenharmony_ci { 0x54, 0x89 }, 1708c2ecf20Sopenharmony_ci { 0x55, 0xab }, 1718c2ecf20Sopenharmony_ci { 0x56, 0x01 }, 1728c2ecf20Sopenharmony_ci { 0x57, 0x23 }, 1738c2ecf20Sopenharmony_ci { 0x58, 0x45 }, 1748c2ecf20Sopenharmony_ci { 0x59, 0x67 }, 1758c2ecf20Sopenharmony_ci { 0x5a, 0x89 }, 1768c2ecf20Sopenharmony_ci { 0x5b, 0xab }, 1778c2ecf20Sopenharmony_ci { 0x5c, 0xcd }, 1788c2ecf20Sopenharmony_ci { 0x5d, 0xef }, 1798c2ecf20Sopenharmony_ci { 0x5e, 0x11 }, 1808c2ecf20Sopenharmony_ci { 0x5f, 0x01 }, 1818c2ecf20Sopenharmony_ci { 0x60, 0x00 }, 1828c2ecf20Sopenharmony_ci { 0x61, 0x15 }, 1838c2ecf20Sopenharmony_ci { 0x62, 0x14 }, 1848c2ecf20Sopenharmony_ci { 0x63, 0x0E }, 1858c2ecf20Sopenharmony_ci { 0x64, 0x0F }, 1868c2ecf20Sopenharmony_ci { 0x65, 0x0C }, 1878c2ecf20Sopenharmony_ci { 0x66, 0x0D }, 1888c2ecf20Sopenharmony_ci { 0x67, 0x06 }, 1898c2ecf20Sopenharmony_ci { 0x68, 0x02 }, 1908c2ecf20Sopenharmony_ci { 0x69, 0x07 }, 1918c2ecf20Sopenharmony_ci { 0x6a, 0x02 }, 1928c2ecf20Sopenharmony_ci { 0x6b, 0x02 }, 1938c2ecf20Sopenharmony_ci { 0x6c, 0x02 }, 1948c2ecf20Sopenharmony_ci { 0x6d, 0x02 }, 1958c2ecf20Sopenharmony_ci { 0x6e, 0x02 }, 1968c2ecf20Sopenharmony_ci { 0x6f, 0x02 }, 1978c2ecf20Sopenharmony_ci { 0x70, 0x02 }, 1988c2ecf20Sopenharmony_ci { 0x71, 0x02 }, 1998c2ecf20Sopenharmony_ci { 0x72, 0x02 }, 2008c2ecf20Sopenharmony_ci { 0x73, 0x02 }, 2018c2ecf20Sopenharmony_ci { 0x74, 0x02 }, 2028c2ecf20Sopenharmony_ci { 0x75, 0x01 }, 2038c2ecf20Sopenharmony_ci { 0x76, 0x00 }, 2048c2ecf20Sopenharmony_ci { 0x77, 0x14 }, 2058c2ecf20Sopenharmony_ci { 0x78, 0x15 }, 2068c2ecf20Sopenharmony_ci { 0x79, 0x0E }, 2078c2ecf20Sopenharmony_ci { 0x7a, 0x0F }, 2088c2ecf20Sopenharmony_ci { 0x7b, 0x0C }, 2098c2ecf20Sopenharmony_ci { 0x7c, 0x0D }, 2108c2ecf20Sopenharmony_ci { 0x7d, 0x06 }, 2118c2ecf20Sopenharmony_ci { 0x7e, 0x02 }, 2128c2ecf20Sopenharmony_ci { 0x7f, 0x07 }, 2138c2ecf20Sopenharmony_ci { 0x80, 0x02 }, 2148c2ecf20Sopenharmony_ci { 0x81, 0x02 }, 2158c2ecf20Sopenharmony_ci { 0x82, 0x02 }, 2168c2ecf20Sopenharmony_ci { 0x83, 0x02 }, 2178c2ecf20Sopenharmony_ci { 0x84, 0x02 }, 2188c2ecf20Sopenharmony_ci { 0x85, 0x02 }, 2198c2ecf20Sopenharmony_ci { 0x86, 0x02 }, 2208c2ecf20Sopenharmony_ci { 0x87, 0x02 }, 2218c2ecf20Sopenharmony_ci { 0x88, 0x02 }, 2228c2ecf20Sopenharmony_ci { 0x89, 0x02 }, 2238c2ecf20Sopenharmony_ci { 0x8A, 0x02 }, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const struct ltk050h3146w_cmd page4_cmds[] = { 2278c2ecf20Sopenharmony_ci { 0x70, 0x00 }, 2288c2ecf20Sopenharmony_ci { 0x71, 0x00 }, 2298c2ecf20Sopenharmony_ci { 0x82, 0x0F }, /* VGH_MOD clamp level=15v */ 2308c2ecf20Sopenharmony_ci { 0x84, 0x0F }, /* VGH clamp level 15V */ 2318c2ecf20Sopenharmony_ci { 0x85, 0x0D }, /* VGL clamp level (-10V) */ 2328c2ecf20Sopenharmony_ci { 0x32, 0xAC }, 2338c2ecf20Sopenharmony_ci { 0x8C, 0x80 }, 2348c2ecf20Sopenharmony_ci { 0x3C, 0xF5 }, 2358c2ecf20Sopenharmony_ci { 0xB5, 0x07 }, /* GAMMA OP */ 2368c2ecf20Sopenharmony_ci { 0x31, 0x45 }, /* SOURCE OP */ 2378c2ecf20Sopenharmony_ci { 0x3A, 0x24 }, /* PS_EN OFF */ 2388c2ecf20Sopenharmony_ci { 0x88, 0x33 }, /* LVD */ 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic inline 2428c2ecf20Sopenharmony_cistruct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci return container_of(panel, struct ltk050h3146w, panel); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#define dsi_dcs_write_seq(dsi, cmd, seq...) do { \ 2488c2ecf20Sopenharmony_ci static const u8 b[] = { cmd, seq }; \ 2498c2ecf20Sopenharmony_ci int ret; \ 2508c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ 2518c2ecf20Sopenharmony_ci if (ret < 0) \ 2528c2ecf20Sopenharmony_ci return ret; \ 2538c2ecf20Sopenharmony_ci } while (0) 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 2588c2ecf20Sopenharmony_ci int ret; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * Init sequence was supplied by the panel vendor without much 2628c2ecf20Sopenharmony_ci * documentation. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8); 2658c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06, 2668c2ecf20Sopenharmony_ci 0x01); 2678c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5); 2688c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5); 2698c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07); 2728c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f, 2738c2ecf20Sopenharmony_ci 0x28, 0x04, 0xcc, 0xcc, 0xcc); 2748c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04); 2758c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2); 2768c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03); 2778c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12); 2788c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80, 2798c2ecf20Sopenharmony_ci 0x80); 2808c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f, 2818c2ecf20Sopenharmony_ci 0x16, 0x00, 0x00); 2828c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50, 2838c2ecf20Sopenharmony_ci 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f, 2848c2ecf20Sopenharmony_ci 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67, 2858c2ecf20Sopenharmony_ci 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55, 2868c2ecf20Sopenharmony_ci 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08); 2878c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a, 2888c2ecf20Sopenharmony_ci 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f, 2898c2ecf20Sopenharmony_ci 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 2908c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b, 2918c2ecf20Sopenharmony_ci 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f, 2928c2ecf20Sopenharmony_ci 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 2938c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05, 2948c2ecf20Sopenharmony_ci 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f, 2958c2ecf20Sopenharmony_ci 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 2968c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04, 2978c2ecf20Sopenharmony_ci 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f, 2988c2ecf20Sopenharmony_ci 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f); 2998c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20, 3008c2ecf20Sopenharmony_ci 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03, 3018c2ecf20Sopenharmony_ci 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08); 3028c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00, 3038c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05, 3048c2ecf20Sopenharmony_ci 0x21, 0x00, 0x60); 3058c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00); 3068c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xde, 0x02); 3078c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c); 3088c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04); 3098c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xc1, 0x11); 3108c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37); 3118c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84); 3128c2ecf20Sopenharmony_ci dsi_dcs_write_seq(dsi, 0xde, 0x00); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_tear_on(dsi, 1); 3158c2ecf20Sopenharmony_ci if (ret < 0) { 3168c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to set tear on: %d\n", ret); 3178c2ecf20Sopenharmony_ci return ret; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci msleep(60); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic const struct drm_display_mode ltk050h3146w_mode = { 3268c2ecf20Sopenharmony_ci .hdisplay = 720, 3278c2ecf20Sopenharmony_ci .hsync_start = 720 + 42, 3288c2ecf20Sopenharmony_ci .hsync_end = 720 + 42 + 8, 3298c2ecf20Sopenharmony_ci .htotal = 720 + 42 + 8 + 42, 3308c2ecf20Sopenharmony_ci .vdisplay = 1280, 3318c2ecf20Sopenharmony_ci .vsync_start = 1280 + 12, 3328c2ecf20Sopenharmony_ci .vsync_end = 1280 + 12 + 4, 3338c2ecf20Sopenharmony_ci .vtotal = 1280 + 12 + 4 + 18, 3348c2ecf20Sopenharmony_ci .clock = 64018, 3358c2ecf20Sopenharmony_ci .width_mm = 62, 3368c2ecf20Sopenharmony_ci .height_mm = 110, 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic const struct ltk050h3146w_desc ltk050h3146w_data = { 3408c2ecf20Sopenharmony_ci .mode = <k050h3146w_mode, 3418c2ecf20Sopenharmony_ci .init = ltk050h3146w_init_sequence, 3428c2ecf20Sopenharmony_ci}; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 3478c2ecf20Sopenharmony_ci u8 d[3] = { 0x98, 0x81, page }; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d)); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page, 3538c2ecf20Sopenharmony_ci const struct ltk050h3146w_cmd *cmds, 3548c2ecf20Sopenharmony_ci int num) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 3578c2ecf20Sopenharmony_ci int i, ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ret = ltk050h3146w_a2_select_page(ctx, page); 3608c2ecf20Sopenharmony_ci if (ret < 0) { 3618c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret); 3628c2ecf20Sopenharmony_ci return ret; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 3668c2ecf20Sopenharmony_ci ret = mipi_dsi_generic_write(dsi, &cmds[i], 3678c2ecf20Sopenharmony_ci sizeof(struct ltk050h3146w_cmd)); 3688c2ecf20Sopenharmony_ci if (ret < 0) { 3698c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret); 3708c2ecf20Sopenharmony_ci return ret; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 3808c2ecf20Sopenharmony_ci int ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * Init sequence was supplied by the panel vendor without much 3848c2ecf20Sopenharmony_ci * documentation. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds, 3878c2ecf20Sopenharmony_ci ARRAY_SIZE(page3_cmds)); 3888c2ecf20Sopenharmony_ci if (ret < 0) 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds, 3928c2ecf20Sopenharmony_ci ARRAY_SIZE(page4_cmds)); 3938c2ecf20Sopenharmony_ci if (ret < 0) 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds, 3978c2ecf20Sopenharmony_ci ARRAY_SIZE(page1_cmds)); 3988c2ecf20Sopenharmony_ci if (ret < 0) 3998c2ecf20Sopenharmony_ci return ret; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci ret = ltk050h3146w_a2_select_page(ctx, 0); 4028c2ecf20Sopenharmony_ci if (ret < 0) { 4038c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to select page 0: %d\n", ret); 4048c2ecf20Sopenharmony_ci return ret; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* vendor code called this without param, where there should be one */ 4088c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_tear_on(dsi, 0); 4098c2ecf20Sopenharmony_ci if (ret < 0) { 4108c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to set tear on: %d\n", ret); 4118c2ecf20Sopenharmony_ci return ret; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci msleep(60); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic const struct drm_display_mode ltk050h3146w_a2_mode = { 4208c2ecf20Sopenharmony_ci .hdisplay = 720, 4218c2ecf20Sopenharmony_ci .hsync_start = 720 + 42, 4228c2ecf20Sopenharmony_ci .hsync_end = 720 + 42 + 10, 4238c2ecf20Sopenharmony_ci .htotal = 720 + 42 + 10 + 60, 4248c2ecf20Sopenharmony_ci .vdisplay = 1280, 4258c2ecf20Sopenharmony_ci .vsync_start = 1280 + 18, 4268c2ecf20Sopenharmony_ci .vsync_end = 1280 + 18 + 4, 4278c2ecf20Sopenharmony_ci .vtotal = 1280 + 18 + 4 + 12, 4288c2ecf20Sopenharmony_ci .clock = 65595, 4298c2ecf20Sopenharmony_ci .width_mm = 62, 4308c2ecf20Sopenharmony_ci .height_mm = 110, 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic const struct ltk050h3146w_desc ltk050h3146w_a2_data = { 4348c2ecf20Sopenharmony_ci .mode = <k050h3146w_a2_mode, 4358c2ecf20Sopenharmony_ci .init = ltk050h3146w_a2_init_sequence, 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int ltk050h3146w_unprepare(struct drm_panel *panel) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 4418c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 4428c2ecf20Sopenharmony_ci int ret; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (!ctx->prepared) 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_off(dsi); 4488c2ecf20Sopenharmony_ci if (ret < 0) { 4498c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to set display off: %d\n", ret); 4508c2ecf20Sopenharmony_ci return ret; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mipi_dsi_dcs_enter_sleep_mode(dsi); 4548c2ecf20Sopenharmony_ci if (ret < 0) { 4558c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret); 4568c2ecf20Sopenharmony_ci return ret; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci regulator_disable(ctx->iovcc); 4608c2ecf20Sopenharmony_ci regulator_disable(ctx->vci); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ctx->prepared = false; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int ltk050h3146w_prepare(struct drm_panel *panel) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 4708c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 4718c2ecf20Sopenharmony_ci int ret; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (ctx->prepared) 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci dev_dbg(ctx->dev, "Resetting the panel\n"); 4778c2ecf20Sopenharmony_ci ret = regulator_enable(ctx->vci); 4788c2ecf20Sopenharmony_ci if (ret < 0) { 4798c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret); 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci ret = regulator_enable(ctx->iovcc); 4838c2ecf20Sopenharmony_ci if (ret < 0) { 4848c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret); 4858c2ecf20Sopenharmony_ci goto disable_vci; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 1); 4898c2ecf20Sopenharmony_ci usleep_range(5000, 6000); 4908c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 0); 4918c2ecf20Sopenharmony_ci msleep(20); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ret = ctx->panel_desc->init(ctx); 4948c2ecf20Sopenharmony_ci if (ret < 0) { 4958c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret); 4968c2ecf20Sopenharmony_ci goto disable_iovcc; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 5008c2ecf20Sopenharmony_ci if (ret < 0) { 5018c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret); 5028c2ecf20Sopenharmony_ci goto disable_iovcc; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* T9: 120ms */ 5068c2ecf20Sopenharmony_ci msleep(120); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_on(dsi); 5098c2ecf20Sopenharmony_ci if (ret < 0) { 5108c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to set display on: %d\n", ret); 5118c2ecf20Sopenharmony_ci goto disable_iovcc; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci msleep(50); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ctx->prepared = true; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cidisable_iovcc: 5218c2ecf20Sopenharmony_ci regulator_disable(ctx->iovcc); 5228c2ecf20Sopenharmony_cidisable_vci: 5238c2ecf20Sopenharmony_ci regulator_disable(ctx->vci); 5248c2ecf20Sopenharmony_ci return ret; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic int ltk050h3146w_get_modes(struct drm_panel *panel, 5288c2ecf20Sopenharmony_ci struct drm_connector *connector) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel); 5318c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode); 5348c2ecf20Sopenharmony_ci if (!mode) 5358c2ecf20Sopenharmony_ci return -ENOMEM; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci drm_mode_set_name(mode); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 5408c2ecf20Sopenharmony_ci connector->display_info.width_mm = mode->width_mm; 5418c2ecf20Sopenharmony_ci connector->display_info.height_mm = mode->height_mm; 5428c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return 1; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic const struct drm_panel_funcs ltk050h3146w_funcs = { 5488c2ecf20Sopenharmony_ci .unprepare = ltk050h3146w_unprepare, 5498c2ecf20Sopenharmony_ci .prepare = ltk050h3146w_prepare, 5508c2ecf20Sopenharmony_ci .get_modes = ltk050h3146w_get_modes, 5518c2ecf20Sopenharmony_ci}; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int ltk050h3146w_probe(struct mipi_dsi_device *dsi) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct device *dev = &dsi->dev; 5568c2ecf20Sopenharmony_ci struct ltk050h3146w *ctx; 5578c2ecf20Sopenharmony_ci int ret; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 5608c2ecf20Sopenharmony_ci if (!ctx) 5618c2ecf20Sopenharmony_ci return -ENOMEM; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ctx->panel_desc = of_device_get_match_data(dev); 5648c2ecf20Sopenharmony_ci if (!ctx->panel_desc) 5658c2ecf20Sopenharmony_ci return -EINVAL; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 5688c2ecf20Sopenharmony_ci if (IS_ERR(ctx->reset_gpio)) { 5698c2ecf20Sopenharmony_ci dev_err(dev, "cannot get reset gpio\n"); 5708c2ecf20Sopenharmony_ci return PTR_ERR(ctx->reset_gpio); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci ctx->vci = devm_regulator_get(dev, "vci"); 5748c2ecf20Sopenharmony_ci if (IS_ERR(ctx->vci)) { 5758c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->vci); 5768c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 5778c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request vci regulator: %d\n", ret); 5788c2ecf20Sopenharmony_ci return ret; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci ctx->iovcc = devm_regulator_get(dev, "iovcc"); 5828c2ecf20Sopenharmony_ci if (IS_ERR(ctx->iovcc)) { 5838c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->iovcc); 5848c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 5858c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request iovcc regulator: %d\n", ret); 5868c2ecf20Sopenharmony_ci return ret; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci mipi_dsi_set_drvdata(dsi, ctx); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci ctx->dev = dev; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci dsi->lanes = 4; 5948c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 5958c2ecf20Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 5968c2ecf20Sopenharmony_ci MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs, 5998c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci ret = drm_panel_of_backlight(&ctx->panel); 6028c2ecf20Sopenharmony_ci if (ret) 6038c2ecf20Sopenharmony_ci return ret; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci drm_panel_add(&ctx->panel); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci ret = mipi_dsi_attach(dsi); 6088c2ecf20Sopenharmony_ci if (ret < 0) { 6098c2ecf20Sopenharmony_ci dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); 6108c2ecf20Sopenharmony_ci drm_panel_remove(&ctx->panel); 6118c2ecf20Sopenharmony_ci return ret; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); 6208c2ecf20Sopenharmony_ci int ret; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ret = drm_panel_unprepare(&ctx->panel); 6238c2ecf20Sopenharmony_ci if (ret < 0) 6248c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci ret = drm_panel_disable(&ctx->panel); 6278c2ecf20Sopenharmony_ci if (ret < 0) 6288c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic int ltk050h3146w_remove(struct mipi_dsi_device *dsi) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); 6348c2ecf20Sopenharmony_ci int ret; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci ltk050h3146w_shutdown(dsi); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ret = mipi_dsi_detach(dsi); 6398c2ecf20Sopenharmony_ci if (ret < 0) 6408c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci drm_panel_remove(&ctx->panel); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic const struct of_device_id ltk050h3146w_of_match[] = { 6488c2ecf20Sopenharmony_ci { 6498c2ecf20Sopenharmony_ci .compatible = "leadtek,ltk050h3146w", 6508c2ecf20Sopenharmony_ci .data = <k050h3146w_data, 6518c2ecf20Sopenharmony_ci }, 6528c2ecf20Sopenharmony_ci { 6538c2ecf20Sopenharmony_ci .compatible = "leadtek,ltk050h3146w-a2", 6548c2ecf20Sopenharmony_ci .data = <k050h3146w_a2_data, 6558c2ecf20Sopenharmony_ci }, 6568c2ecf20Sopenharmony_ci { /* sentinel */ } 6578c2ecf20Sopenharmony_ci}; 6588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ltk050h3146w_of_match); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic struct mipi_dsi_driver ltk050h3146w_driver = { 6618c2ecf20Sopenharmony_ci .driver = { 6628c2ecf20Sopenharmony_ci .name = "panel-leadtek-ltk050h3146w", 6638c2ecf20Sopenharmony_ci .of_match_table = ltk050h3146w_of_match, 6648c2ecf20Sopenharmony_ci }, 6658c2ecf20Sopenharmony_ci .probe = ltk050h3146w_probe, 6668c2ecf20Sopenharmony_ci .remove = ltk050h3146w_remove, 6678c2ecf20Sopenharmony_ci .shutdown = ltk050h3146w_shutdown, 6688c2ecf20Sopenharmony_ci}; 6698c2ecf20Sopenharmony_cimodule_mipi_dsi_driver(ltk050h3146w_driver); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 6728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel"); 6738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 674