18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2019 Theobroma Systems Design und Consulting GmbH 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * base on panel-kingdisplay-kd097d04.c 68c2ecf20Sopenharmony_ci * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/backlight.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <video/mipi_display.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_modes.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct ltk500hd1829 { 258c2ecf20Sopenharmony_ci struct device *dev; 268c2ecf20Sopenharmony_ci struct drm_panel panel; 278c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 288c2ecf20Sopenharmony_ci struct regulator *vcc; 298c2ecf20Sopenharmony_ci struct regulator *iovcc; 308c2ecf20Sopenharmony_ci bool prepared; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct ltk500hd1829_cmd { 348c2ecf20Sopenharmony_ci char cmd; 358c2ecf20Sopenharmony_ci char data; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * There is no description in the Reference Manual about these commands. 408c2ecf20Sopenharmony_ci * We received them from the vendor, so just use them as is. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic const struct ltk500hd1829_cmd init_code[] = { 438c2ecf20Sopenharmony_ci { 0xE0, 0x00 }, 448c2ecf20Sopenharmony_ci { 0xE1, 0x93 }, 458c2ecf20Sopenharmony_ci { 0xE2, 0x65 }, 468c2ecf20Sopenharmony_ci { 0xE3, 0xF8 }, 478c2ecf20Sopenharmony_ci { 0x80, 0x03 }, 488c2ecf20Sopenharmony_ci { 0xE0, 0x04 }, 498c2ecf20Sopenharmony_ci { 0x2D, 0x03 }, 508c2ecf20Sopenharmony_ci { 0xE0, 0x01 }, 518c2ecf20Sopenharmony_ci { 0x00, 0x00 }, 528c2ecf20Sopenharmony_ci { 0x01, 0xB6 }, 538c2ecf20Sopenharmony_ci { 0x03, 0x00 }, 548c2ecf20Sopenharmony_ci { 0x04, 0xC5 }, 558c2ecf20Sopenharmony_ci { 0x17, 0x00 }, 568c2ecf20Sopenharmony_ci { 0x18, 0xBF }, 578c2ecf20Sopenharmony_ci { 0x19, 0x01 }, 588c2ecf20Sopenharmony_ci { 0x1A, 0x00 }, 598c2ecf20Sopenharmony_ci { 0x1B, 0xBF }, 608c2ecf20Sopenharmony_ci { 0x1C, 0x01 }, 618c2ecf20Sopenharmony_ci { 0x1F, 0x7C }, 628c2ecf20Sopenharmony_ci { 0x20, 0x26 }, 638c2ecf20Sopenharmony_ci { 0x21, 0x26 }, 648c2ecf20Sopenharmony_ci { 0x22, 0x4E }, 658c2ecf20Sopenharmony_ci { 0x37, 0x09 }, 668c2ecf20Sopenharmony_ci { 0x38, 0x04 }, 678c2ecf20Sopenharmony_ci { 0x39, 0x08 }, 688c2ecf20Sopenharmony_ci { 0x3A, 0x1F }, 698c2ecf20Sopenharmony_ci { 0x3B, 0x1F }, 708c2ecf20Sopenharmony_ci { 0x3C, 0x78 }, 718c2ecf20Sopenharmony_ci { 0x3D, 0xFF }, 728c2ecf20Sopenharmony_ci { 0x3E, 0xFF }, 738c2ecf20Sopenharmony_ci { 0x3F, 0x00 }, 748c2ecf20Sopenharmony_ci { 0x40, 0x04 }, 758c2ecf20Sopenharmony_ci { 0x41, 0xA0 }, 768c2ecf20Sopenharmony_ci { 0x43, 0x0F }, 778c2ecf20Sopenharmony_ci { 0x44, 0x0A }, 788c2ecf20Sopenharmony_ci { 0x45, 0x24 }, 798c2ecf20Sopenharmony_ci { 0x55, 0x01 }, 808c2ecf20Sopenharmony_ci { 0x56, 0x01 }, 818c2ecf20Sopenharmony_ci { 0x57, 0xA5 }, 828c2ecf20Sopenharmony_ci { 0x58, 0x0A }, 838c2ecf20Sopenharmony_ci { 0x59, 0x4A }, 848c2ecf20Sopenharmony_ci { 0x5A, 0x38 }, 858c2ecf20Sopenharmony_ci { 0x5B, 0x10 }, 868c2ecf20Sopenharmony_ci { 0x5C, 0x19 }, 878c2ecf20Sopenharmony_ci { 0x5D, 0x7C }, 888c2ecf20Sopenharmony_ci { 0x5E, 0x64 }, 898c2ecf20Sopenharmony_ci { 0x5F, 0x54 }, 908c2ecf20Sopenharmony_ci { 0x60, 0x48 }, 918c2ecf20Sopenharmony_ci { 0x61, 0x44 }, 928c2ecf20Sopenharmony_ci { 0x62, 0x35 }, 938c2ecf20Sopenharmony_ci { 0x63, 0x3A }, 948c2ecf20Sopenharmony_ci { 0x64, 0x24 }, 958c2ecf20Sopenharmony_ci { 0x65, 0x3B }, 968c2ecf20Sopenharmony_ci { 0x66, 0x39 }, 978c2ecf20Sopenharmony_ci { 0x67, 0x37 }, 988c2ecf20Sopenharmony_ci { 0x68, 0x56 }, 998c2ecf20Sopenharmony_ci { 0x69, 0x41 }, 1008c2ecf20Sopenharmony_ci { 0x6A, 0x47 }, 1018c2ecf20Sopenharmony_ci { 0x6B, 0x2F }, 1028c2ecf20Sopenharmony_ci { 0x6C, 0x23 }, 1038c2ecf20Sopenharmony_ci { 0x6D, 0x13 }, 1048c2ecf20Sopenharmony_ci { 0x6E, 0x02 }, 1058c2ecf20Sopenharmony_ci { 0x6F, 0x08 }, 1068c2ecf20Sopenharmony_ci { 0x70, 0x7C }, 1078c2ecf20Sopenharmony_ci { 0x71, 0x64 }, 1088c2ecf20Sopenharmony_ci { 0x72, 0x54 }, 1098c2ecf20Sopenharmony_ci { 0x73, 0x48 }, 1108c2ecf20Sopenharmony_ci { 0x74, 0x44 }, 1118c2ecf20Sopenharmony_ci { 0x75, 0x35 }, 1128c2ecf20Sopenharmony_ci { 0x76, 0x3A }, 1138c2ecf20Sopenharmony_ci { 0x77, 0x22 }, 1148c2ecf20Sopenharmony_ci { 0x78, 0x3B }, 1158c2ecf20Sopenharmony_ci { 0x79, 0x39 }, 1168c2ecf20Sopenharmony_ci { 0x7A, 0x38 }, 1178c2ecf20Sopenharmony_ci { 0x7B, 0x52 }, 1188c2ecf20Sopenharmony_ci { 0x7C, 0x41 }, 1198c2ecf20Sopenharmony_ci { 0x7D, 0x47 }, 1208c2ecf20Sopenharmony_ci { 0x7E, 0x2F }, 1218c2ecf20Sopenharmony_ci { 0x7F, 0x23 }, 1228c2ecf20Sopenharmony_ci { 0x80, 0x13 }, 1238c2ecf20Sopenharmony_ci { 0x81, 0x02 }, 1248c2ecf20Sopenharmony_ci { 0x82, 0x08 }, 1258c2ecf20Sopenharmony_ci { 0xE0, 0x02 }, 1268c2ecf20Sopenharmony_ci { 0x00, 0x57 }, 1278c2ecf20Sopenharmony_ci { 0x01, 0x77 }, 1288c2ecf20Sopenharmony_ci { 0x02, 0x44 }, 1298c2ecf20Sopenharmony_ci { 0x03, 0x46 }, 1308c2ecf20Sopenharmony_ci { 0x04, 0x48 }, 1318c2ecf20Sopenharmony_ci { 0x05, 0x4A }, 1328c2ecf20Sopenharmony_ci { 0x06, 0x4C }, 1338c2ecf20Sopenharmony_ci { 0x07, 0x4E }, 1348c2ecf20Sopenharmony_ci { 0x08, 0x50 }, 1358c2ecf20Sopenharmony_ci { 0x09, 0x55 }, 1368c2ecf20Sopenharmony_ci { 0x0A, 0x52 }, 1378c2ecf20Sopenharmony_ci { 0x0B, 0x55 }, 1388c2ecf20Sopenharmony_ci { 0x0C, 0x55 }, 1398c2ecf20Sopenharmony_ci { 0x0D, 0x55 }, 1408c2ecf20Sopenharmony_ci { 0x0E, 0x55 }, 1418c2ecf20Sopenharmony_ci { 0x0F, 0x55 }, 1428c2ecf20Sopenharmony_ci { 0x10, 0x55 }, 1438c2ecf20Sopenharmony_ci { 0x11, 0x55 }, 1448c2ecf20Sopenharmony_ci { 0x12, 0x55 }, 1458c2ecf20Sopenharmony_ci { 0x13, 0x40 }, 1468c2ecf20Sopenharmony_ci { 0x14, 0x55 }, 1478c2ecf20Sopenharmony_ci { 0x15, 0x55 }, 1488c2ecf20Sopenharmony_ci { 0x16, 0x57 }, 1498c2ecf20Sopenharmony_ci { 0x17, 0x77 }, 1508c2ecf20Sopenharmony_ci { 0x18, 0x45 }, 1518c2ecf20Sopenharmony_ci { 0x19, 0x47 }, 1528c2ecf20Sopenharmony_ci { 0x1A, 0x49 }, 1538c2ecf20Sopenharmony_ci { 0x1B, 0x4B }, 1548c2ecf20Sopenharmony_ci { 0x1C, 0x4D }, 1558c2ecf20Sopenharmony_ci { 0x1D, 0x4F }, 1568c2ecf20Sopenharmony_ci { 0x1E, 0x51 }, 1578c2ecf20Sopenharmony_ci { 0x1F, 0x55 }, 1588c2ecf20Sopenharmony_ci { 0x20, 0x53 }, 1598c2ecf20Sopenharmony_ci { 0x21, 0x55 }, 1608c2ecf20Sopenharmony_ci { 0x22, 0x55 }, 1618c2ecf20Sopenharmony_ci { 0x23, 0x55 }, 1628c2ecf20Sopenharmony_ci { 0x24, 0x55 }, 1638c2ecf20Sopenharmony_ci { 0x25, 0x55 }, 1648c2ecf20Sopenharmony_ci { 0x26, 0x55 }, 1658c2ecf20Sopenharmony_ci { 0x27, 0x55 }, 1668c2ecf20Sopenharmony_ci { 0x28, 0x55 }, 1678c2ecf20Sopenharmony_ci { 0x29, 0x41 }, 1688c2ecf20Sopenharmony_ci { 0x2A, 0x55 }, 1698c2ecf20Sopenharmony_ci { 0x2B, 0x55 }, 1708c2ecf20Sopenharmony_ci { 0x2C, 0x57 }, 1718c2ecf20Sopenharmony_ci { 0x2D, 0x77 }, 1728c2ecf20Sopenharmony_ci { 0x2E, 0x4F }, 1738c2ecf20Sopenharmony_ci { 0x2F, 0x4D }, 1748c2ecf20Sopenharmony_ci { 0x30, 0x4B }, 1758c2ecf20Sopenharmony_ci { 0x31, 0x49 }, 1768c2ecf20Sopenharmony_ci { 0x32, 0x47 }, 1778c2ecf20Sopenharmony_ci { 0x33, 0x45 }, 1788c2ecf20Sopenharmony_ci { 0x34, 0x41 }, 1798c2ecf20Sopenharmony_ci { 0x35, 0x55 }, 1808c2ecf20Sopenharmony_ci { 0x36, 0x53 }, 1818c2ecf20Sopenharmony_ci { 0x37, 0x55 }, 1828c2ecf20Sopenharmony_ci { 0x38, 0x55 }, 1838c2ecf20Sopenharmony_ci { 0x39, 0x55 }, 1848c2ecf20Sopenharmony_ci { 0x3A, 0x55 }, 1858c2ecf20Sopenharmony_ci { 0x3B, 0x55 }, 1868c2ecf20Sopenharmony_ci { 0x3C, 0x55 }, 1878c2ecf20Sopenharmony_ci { 0x3D, 0x55 }, 1888c2ecf20Sopenharmony_ci { 0x3E, 0x55 }, 1898c2ecf20Sopenharmony_ci { 0x3F, 0x51 }, 1908c2ecf20Sopenharmony_ci { 0x40, 0x55 }, 1918c2ecf20Sopenharmony_ci { 0x41, 0x55 }, 1928c2ecf20Sopenharmony_ci { 0x42, 0x57 }, 1938c2ecf20Sopenharmony_ci { 0x43, 0x77 }, 1948c2ecf20Sopenharmony_ci { 0x44, 0x4E }, 1958c2ecf20Sopenharmony_ci { 0x45, 0x4C }, 1968c2ecf20Sopenharmony_ci { 0x46, 0x4A }, 1978c2ecf20Sopenharmony_ci { 0x47, 0x48 }, 1988c2ecf20Sopenharmony_ci { 0x48, 0x46 }, 1998c2ecf20Sopenharmony_ci { 0x49, 0x44 }, 2008c2ecf20Sopenharmony_ci { 0x4A, 0x40 }, 2018c2ecf20Sopenharmony_ci { 0x4B, 0x55 }, 2028c2ecf20Sopenharmony_ci { 0x4C, 0x52 }, 2038c2ecf20Sopenharmony_ci { 0x4D, 0x55 }, 2048c2ecf20Sopenharmony_ci { 0x4E, 0x55 }, 2058c2ecf20Sopenharmony_ci { 0x4F, 0x55 }, 2068c2ecf20Sopenharmony_ci { 0x50, 0x55 }, 2078c2ecf20Sopenharmony_ci { 0x51, 0x55 }, 2088c2ecf20Sopenharmony_ci { 0x52, 0x55 }, 2098c2ecf20Sopenharmony_ci { 0x53, 0x55 }, 2108c2ecf20Sopenharmony_ci { 0x54, 0x55 }, 2118c2ecf20Sopenharmony_ci { 0x55, 0x50 }, 2128c2ecf20Sopenharmony_ci { 0x56, 0x55 }, 2138c2ecf20Sopenharmony_ci { 0x57, 0x55 }, 2148c2ecf20Sopenharmony_ci { 0x58, 0x40 }, 2158c2ecf20Sopenharmony_ci { 0x59, 0x00 }, 2168c2ecf20Sopenharmony_ci { 0x5A, 0x00 }, 2178c2ecf20Sopenharmony_ci { 0x5B, 0x10 }, 2188c2ecf20Sopenharmony_ci { 0x5C, 0x09 }, 2198c2ecf20Sopenharmony_ci { 0x5D, 0x30 }, 2208c2ecf20Sopenharmony_ci { 0x5E, 0x01 }, 2218c2ecf20Sopenharmony_ci { 0x5F, 0x02 }, 2228c2ecf20Sopenharmony_ci { 0x60, 0x30 }, 2238c2ecf20Sopenharmony_ci { 0x61, 0x03 }, 2248c2ecf20Sopenharmony_ci { 0x62, 0x04 }, 2258c2ecf20Sopenharmony_ci { 0x63, 0x06 }, 2268c2ecf20Sopenharmony_ci { 0x64, 0x6A }, 2278c2ecf20Sopenharmony_ci { 0x65, 0x75 }, 2288c2ecf20Sopenharmony_ci { 0x66, 0x0F }, 2298c2ecf20Sopenharmony_ci { 0x67, 0xB3 }, 2308c2ecf20Sopenharmony_ci { 0x68, 0x0B }, 2318c2ecf20Sopenharmony_ci { 0x69, 0x06 }, 2328c2ecf20Sopenharmony_ci { 0x6A, 0x6A }, 2338c2ecf20Sopenharmony_ci { 0x6B, 0x10 }, 2348c2ecf20Sopenharmony_ci { 0x6C, 0x00 }, 2358c2ecf20Sopenharmony_ci { 0x6D, 0x04 }, 2368c2ecf20Sopenharmony_ci { 0x6E, 0x04 }, 2378c2ecf20Sopenharmony_ci { 0x6F, 0x88 }, 2388c2ecf20Sopenharmony_ci { 0x70, 0x00 }, 2398c2ecf20Sopenharmony_ci { 0x71, 0x00 }, 2408c2ecf20Sopenharmony_ci { 0x72, 0x06 }, 2418c2ecf20Sopenharmony_ci { 0x73, 0x7B }, 2428c2ecf20Sopenharmony_ci { 0x74, 0x00 }, 2438c2ecf20Sopenharmony_ci { 0x75, 0xBC }, 2448c2ecf20Sopenharmony_ci { 0x76, 0x00 }, 2458c2ecf20Sopenharmony_ci { 0x77, 0x05 }, 2468c2ecf20Sopenharmony_ci { 0x78, 0x2E }, 2478c2ecf20Sopenharmony_ci { 0x79, 0x00 }, 2488c2ecf20Sopenharmony_ci { 0x7A, 0x00 }, 2498c2ecf20Sopenharmony_ci { 0x7B, 0x00 }, 2508c2ecf20Sopenharmony_ci { 0x7C, 0x00 }, 2518c2ecf20Sopenharmony_ci { 0x7D, 0x03 }, 2528c2ecf20Sopenharmony_ci { 0x7E, 0x7B }, 2538c2ecf20Sopenharmony_ci { 0xE0, 0x04 }, 2548c2ecf20Sopenharmony_ci { 0x09, 0x10 }, 2558c2ecf20Sopenharmony_ci { 0x2B, 0x2B }, 2568c2ecf20Sopenharmony_ci { 0x2E, 0x44 }, 2578c2ecf20Sopenharmony_ci { 0xE0, 0x00 }, 2588c2ecf20Sopenharmony_ci { 0xE6, 0x02 }, 2598c2ecf20Sopenharmony_ci { 0xE7, 0x02 }, 2608c2ecf20Sopenharmony_ci { 0x35, 0x00 }, 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic inline 2648c2ecf20Sopenharmony_cistruct ltk500hd1829 *panel_to_ltk500hd1829(struct drm_panel *panel) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci return container_of(panel, struct ltk500hd1829, panel); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int ltk500hd1829_unprepare(struct drm_panel *panel) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); 2728c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 2738c2ecf20Sopenharmony_ci int ret; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!ctx->prepared) 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_off(dsi); 2798c2ecf20Sopenharmony_ci if (ret < 0) 2808c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to set display off: %d\n", ret); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 2838c2ecf20Sopenharmony_ci if (ret < 0) { 2848c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* 120ms to enter sleep mode */ 2888c2ecf20Sopenharmony_ci msleep(120); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci regulator_disable(ctx->iovcc); 2918c2ecf20Sopenharmony_ci regulator_disable(ctx->vcc); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ctx->prepared = false; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int ltk500hd1829_prepare(struct drm_panel *panel) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); 3018c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 3028c2ecf20Sopenharmony_ci unsigned int i; 3038c2ecf20Sopenharmony_ci int ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (ctx->prepared) 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = regulator_enable(ctx->vcc); 3098c2ecf20Sopenharmony_ci if (ret < 0) { 3108c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret); 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci ret = regulator_enable(ctx->iovcc); 3148c2ecf20Sopenharmony_ci if (ret < 0) { 3158c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret); 3168c2ecf20Sopenharmony_ci goto disable_vcc; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 1); 3208c2ecf20Sopenharmony_ci /* tRW: 10us */ 3218c2ecf20Sopenharmony_ci usleep_range(10, 20); 3228c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 0); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* tRT: >= 5ms */ 3258c2ecf20Sopenharmony_ci usleep_range(5000, 6000); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(init_code); i++) { 3288c2ecf20Sopenharmony_ci ret = mipi_dsi_generic_write(dsi, &init_code[i], 3298c2ecf20Sopenharmony_ci sizeof(struct ltk500hd1829_cmd)); 3308c2ecf20Sopenharmony_ci if (ret < 0) { 3318c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to write init cmds: %d\n", ret); 3328c2ecf20Sopenharmony_ci goto disable_iovcc; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 3378c2ecf20Sopenharmony_ci if (ret < 0) { 3388c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to exit sleep mode: %d\n", ret); 3398c2ecf20Sopenharmony_ci goto disable_iovcc; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* 120ms to exit sleep mode */ 3438c2ecf20Sopenharmony_ci msleep(120); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_on(dsi); 3468c2ecf20Sopenharmony_ci if (ret < 0) { 3478c2ecf20Sopenharmony_ci dev_err(panel->dev, "failed to set display on: %d\n", ret); 3488c2ecf20Sopenharmony_ci goto disable_iovcc; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ctx->prepared = true; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cidisable_iovcc: 3568c2ecf20Sopenharmony_ci regulator_disable(ctx->iovcc); 3578c2ecf20Sopenharmony_cidisable_vcc: 3588c2ecf20Sopenharmony_ci regulator_disable(ctx->vcc); 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic const struct drm_display_mode default_mode = { 3638c2ecf20Sopenharmony_ci .hdisplay = 720, 3648c2ecf20Sopenharmony_ci .hsync_start = 720 + 50, 3658c2ecf20Sopenharmony_ci .hsync_end = 720 + 50 + 50, 3668c2ecf20Sopenharmony_ci .htotal = 720 + 50 + 50 + 50, 3678c2ecf20Sopenharmony_ci .vdisplay = 1280, 3688c2ecf20Sopenharmony_ci .vsync_start = 1280 + 30, 3698c2ecf20Sopenharmony_ci .vsync_end = 1280 + 30 + 4, 3708c2ecf20Sopenharmony_ci .vtotal = 1280 + 30 + 4 + 12, 3718c2ecf20Sopenharmony_ci .clock = 69217, 3728c2ecf20Sopenharmony_ci .width_mm = 62, 3738c2ecf20Sopenharmony_ci .height_mm = 110, 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int ltk500hd1829_get_modes(struct drm_panel *panel, 3778c2ecf20Sopenharmony_ci struct drm_connector *connector) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct ltk500hd1829 *ctx = panel_to_ltk500hd1829(panel); 3808c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &default_mode); 3838c2ecf20Sopenharmony_ci if (!mode) { 3848c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to add mode %ux%u@%u\n", 3858c2ecf20Sopenharmony_ci default_mode.hdisplay, default_mode.vdisplay, 3868c2ecf20Sopenharmony_ci drm_mode_vrefresh(&default_mode)); 3878c2ecf20Sopenharmony_ci return -ENOMEM; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci drm_mode_set_name(mode); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 3938c2ecf20Sopenharmony_ci connector->display_info.width_mm = mode->width_mm; 3948c2ecf20Sopenharmony_ci connector->display_info.height_mm = mode->height_mm; 3958c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 1; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic const struct drm_panel_funcs ltk500hd1829_funcs = { 4018c2ecf20Sopenharmony_ci .unprepare = ltk500hd1829_unprepare, 4028c2ecf20Sopenharmony_ci .prepare = ltk500hd1829_prepare, 4038c2ecf20Sopenharmony_ci .get_modes = ltk500hd1829_get_modes, 4048c2ecf20Sopenharmony_ci}; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int ltk500hd1829_probe(struct mipi_dsi_device *dsi) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct ltk500hd1829 *ctx; 4098c2ecf20Sopenharmony_ci struct device *dev = &dsi->dev; 4108c2ecf20Sopenharmony_ci int ret; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); 4138c2ecf20Sopenharmony_ci if (!ctx) 4148c2ecf20Sopenharmony_ci return -ENOMEM; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 4178c2ecf20Sopenharmony_ci if (IS_ERR(ctx->reset_gpio)) { 4188c2ecf20Sopenharmony_ci dev_err(dev, "cannot get reset gpio\n"); 4198c2ecf20Sopenharmony_ci return PTR_ERR(ctx->reset_gpio); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ctx->vcc = devm_regulator_get(dev, "vcc"); 4238c2ecf20Sopenharmony_ci if (IS_ERR(ctx->vcc)) { 4248c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->vcc); 4258c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 4268c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request vcc regulator: %d\n", ret); 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci ctx->iovcc = devm_regulator_get(dev, "iovcc"); 4318c2ecf20Sopenharmony_ci if (IS_ERR(ctx->iovcc)) { 4328c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->iovcc); 4338c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 4348c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request iovcc regulator: %d\n", ret); 4358c2ecf20Sopenharmony_ci return ret; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci mipi_dsi_set_drvdata(dsi, ctx); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ctx->dev = dev; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci dsi->lanes = 4; 4438c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 4448c2ecf20Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 4458c2ecf20Sopenharmony_ci MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci drm_panel_init(&ctx->panel, &dsi->dev, <k500hd1829_funcs, 4488c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ret = drm_panel_of_backlight(&ctx->panel); 4518c2ecf20Sopenharmony_ci if (ret) 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci drm_panel_add(&ctx->panel); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci ret = mipi_dsi_attach(dsi); 4578c2ecf20Sopenharmony_ci if (ret < 0) { 4588c2ecf20Sopenharmony_ci dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); 4598c2ecf20Sopenharmony_ci drm_panel_remove(&ctx->panel); 4608c2ecf20Sopenharmony_ci return ret; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic void ltk500hd1829_shutdown(struct mipi_dsi_device *dsi) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi); 4698c2ecf20Sopenharmony_ci int ret; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ret = drm_panel_unprepare(&ctx->panel); 4728c2ecf20Sopenharmony_ci if (ret < 0) 4738c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci ret = drm_panel_disable(&ctx->panel); 4768c2ecf20Sopenharmony_ci if (ret < 0) 4778c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int ltk500hd1829_remove(struct mipi_dsi_device *dsi) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi); 4838c2ecf20Sopenharmony_ci int ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci ltk500hd1829_shutdown(dsi); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci ret = mipi_dsi_detach(dsi); 4888c2ecf20Sopenharmony_ci if (ret < 0) 4898c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci drm_panel_remove(&ctx->panel); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic const struct of_device_id ltk500hd1829_of_match[] = { 4978c2ecf20Sopenharmony_ci { .compatible = "leadtek,ltk500hd1829", }, 4988c2ecf20Sopenharmony_ci { /* sentinel */ } 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ltk500hd1829_of_match); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic struct mipi_dsi_driver ltk500hd1829_driver = { 5038c2ecf20Sopenharmony_ci .driver = { 5048c2ecf20Sopenharmony_ci .name = "panel-leadtek-ltk500hd1829", 5058c2ecf20Sopenharmony_ci .of_match_table = ltk500hd1829_of_match, 5068c2ecf20Sopenharmony_ci }, 5078c2ecf20Sopenharmony_ci .probe = ltk500hd1829_probe, 5088c2ecf20Sopenharmony_ci .remove = ltk500hd1829_remove, 5098c2ecf20Sopenharmony_ci .shutdown = ltk500hd1829_shutdown, 5108c2ecf20Sopenharmony_ci}; 5118c2ecf20Sopenharmony_cimodule_mipi_dsi_driver(ltk500hd1829_driver); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 5148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Leadtek LTK500HD1829 panel driver"); 5158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 516