18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * based on 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Rockteck jh057n00900 5.5" MIPI-DSI panel driver 98c2ecf20Sopenharmony_ci * Copyright (C) Purism SPC 2019 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_modes.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <video/display_timing.h> 178c2ecf20Sopenharmony_ci#include <video/mipi_display.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 218c2ecf20Sopenharmony_ci#include <linux/media-bus-format.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/of.h> 248c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Manufacturer specific Commands send via DSI */ 278c2ecf20Sopenharmony_ci#define XPP055C272_CMD_ALL_PIXEL_OFF 0x22 288c2ecf20Sopenharmony_ci#define XPP055C272_CMD_ALL_PIXEL_ON 0x23 298c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETDISP 0xb2 308c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETRGBIF 0xb3 318c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETCYC 0xb4 328c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETBGP 0xb5 338c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETVCOM 0xb6 348c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETOTP 0xb7 358c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETPOWER_EXT 0xb8 368c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETEXTC 0xb9 378c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETMIPI 0xbA 388c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETVDC 0xbc 398c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETPCR 0xbf 408c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETSCR 0xc0 418c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETPOWER 0xc1 428c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETECO 0xc6 438c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETPANEL 0xcc 448c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETGAMMA 0xe0 458c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETEQ 0xe3 468c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETGIP1 0xe9 478c2ecf20Sopenharmony_ci#define XPP055C272_CMD_SETGIP2 0xea 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct xpp055c272 { 508c2ecf20Sopenharmony_ci struct device *dev; 518c2ecf20Sopenharmony_ci struct drm_panel panel; 528c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 538c2ecf20Sopenharmony_ci struct regulator *vci; 548c2ecf20Sopenharmony_ci struct regulator *iovcc; 558c2ecf20Sopenharmony_ci bool prepared; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci return container_of(panel, struct xpp055c272, panel); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define dsi_generic_write_seq(dsi, cmd, seq...) do { \ 648c2ecf20Sopenharmony_ci static const u8 b[] = { cmd, seq }; \ 658c2ecf20Sopenharmony_ci int ret; \ 668c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \ 678c2ecf20Sopenharmony_ci if (ret < 0) \ 688c2ecf20Sopenharmony_ci return ret; \ 698c2ecf20Sopenharmony_ci } while (0) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int xpp055c272_init_sequence(struct xpp055c272 *ctx) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 748c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * Init sequence was supplied by the panel vendor without much 788c2ecf20Sopenharmony_ci * documentation. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83); 818c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETMIPI, 828c2ecf20Sopenharmony_ci 0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00, 838c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 848c2ecf20Sopenharmony_ci 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01, 858c2ecf20Sopenharmony_ci 0x00, 0x00, 0x37); 868c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25); 878c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00); 888c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETRGBIF, 898c2ecf20Sopenharmony_ci 0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00, 908c2ecf20Sopenharmony_ci 0x00, 0x00); 918c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETSCR, 928c2ecf20Sopenharmony_ci 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, 938c2ecf20Sopenharmony_ci 0x00); 948c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46); 958c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b); 968c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80); 978c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30); 988c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEQ, 998c2ecf20Sopenharmony_ci 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, 1008c2ecf20Sopenharmony_ci 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); 1018c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER, 1028c2ecf20Sopenharmony_ci 0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd, 1038c2ecf20Sopenharmony_ci 0x67, 0x77, 0x33, 0x33); 1048c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff, 1058c2ecf20Sopenharmony_ci 0xff, 0x01, 0xff); 1068c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09); 1078c2ecf20Sopenharmony_ci msleep(20); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95); 1108c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP1, 1118c2ecf20Sopenharmony_ci 0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12, 1128c2ecf20Sopenharmony_ci 0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18, 1138c2ecf20Sopenharmony_ci 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 1148c2ecf20Sopenharmony_ci 0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42, 1158c2ecf20Sopenharmony_ci 0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58, 1168c2ecf20Sopenharmony_ci 0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88, 1178c2ecf20Sopenharmony_ci 0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 1188c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 1198c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP2, 1208c2ecf20Sopenharmony_ci 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 1218c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35, 1228c2ecf20Sopenharmony_ci 0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f, 1238c2ecf20Sopenharmony_ci 0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88, 1248c2ecf20Sopenharmony_ci 0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00, 1258c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1268c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05, 1278c2ecf20Sopenharmony_ci 0xa0, 0x00, 0x00, 0x00, 0x00); 1288c2ecf20Sopenharmony_ci dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGAMMA, 1298c2ecf20Sopenharmony_ci 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36, 1308c2ecf20Sopenharmony_ci 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11, 1318c2ecf20Sopenharmony_ci 0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 1328c2ecf20Sopenharmony_ci 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 1338c2ecf20Sopenharmony_ci 0x11, 0x18); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci msleep(60); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci dev_dbg(dev, "Panel init sequence done\n"); 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int xpp055c272_unprepare(struct drm_panel *panel) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 1448c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 1458c2ecf20Sopenharmony_ci int ret; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (!ctx->prepared) 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_off(dsi); 1518c2ecf20Sopenharmony_ci if (ret < 0) 1528c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to set display off: %d\n", ret); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci mipi_dsi_dcs_enter_sleep_mode(dsi); 1558c2ecf20Sopenharmony_ci if (ret < 0) { 1568c2ecf20Sopenharmony_ci dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret); 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci regulator_disable(ctx->iovcc); 1618c2ecf20Sopenharmony_ci regulator_disable(ctx->vci); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ctx->prepared = false; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int xpp055c272_prepare(struct drm_panel *panel) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 1718c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 1728c2ecf20Sopenharmony_ci int ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (ctx->prepared) 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci dev_dbg(ctx->dev, "Resetting the panel\n"); 1788c2ecf20Sopenharmony_ci ret = regulator_enable(ctx->vci); 1798c2ecf20Sopenharmony_ci if (ret < 0) { 1808c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret); 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci ret = regulator_enable(ctx->iovcc); 1848c2ecf20Sopenharmony_ci if (ret < 0) { 1858c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret); 1868c2ecf20Sopenharmony_ci goto disable_vci; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 1); 1908c2ecf20Sopenharmony_ci /* T6: 10us */ 1918c2ecf20Sopenharmony_ci usleep_range(10, 20); 1928c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ctx->reset_gpio, 0); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* T8: 20ms */ 1958c2ecf20Sopenharmony_ci msleep(20); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = xpp055c272_init_sequence(ctx); 1988c2ecf20Sopenharmony_ci if (ret < 0) { 1998c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret); 2008c2ecf20Sopenharmony_ci goto disable_iovcc; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 2048c2ecf20Sopenharmony_ci if (ret < 0) { 2058c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret); 2068c2ecf20Sopenharmony_ci goto disable_iovcc; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* T9: 120ms */ 2108c2ecf20Sopenharmony_ci msleep(120); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = mipi_dsi_dcs_set_display_on(dsi); 2138c2ecf20Sopenharmony_ci if (ret < 0) { 2148c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to set display on: %d\n", ret); 2158c2ecf20Sopenharmony_ci goto disable_iovcc; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci msleep(50); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ctx->prepared = true; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cidisable_iovcc: 2258c2ecf20Sopenharmony_ci regulator_disable(ctx->iovcc); 2268c2ecf20Sopenharmony_cidisable_vci: 2278c2ecf20Sopenharmony_ci regulator_disable(ctx->vci); 2288c2ecf20Sopenharmony_ci return ret; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic const struct drm_display_mode default_mode = { 2328c2ecf20Sopenharmony_ci .hdisplay = 720, 2338c2ecf20Sopenharmony_ci .hsync_start = 720 + 40, 2348c2ecf20Sopenharmony_ci .hsync_end = 720 + 40 + 10, 2358c2ecf20Sopenharmony_ci .htotal = 720 + 40 + 10 + 40, 2368c2ecf20Sopenharmony_ci .vdisplay = 1280, 2378c2ecf20Sopenharmony_ci .vsync_start = 1280 + 22, 2388c2ecf20Sopenharmony_ci .vsync_end = 1280 + 22 + 4, 2398c2ecf20Sopenharmony_ci .vtotal = 1280 + 22 + 4 + 11, 2408c2ecf20Sopenharmony_ci .clock = 64000, 2418c2ecf20Sopenharmony_ci .width_mm = 68, 2428c2ecf20Sopenharmony_ci .height_mm = 121, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int xpp055c272_get_modes(struct drm_panel *panel, 2468c2ecf20Sopenharmony_ci struct drm_connector *connector) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 2498c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, &default_mode); 2528c2ecf20Sopenharmony_ci if (!mode) { 2538c2ecf20Sopenharmony_ci dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n", 2548c2ecf20Sopenharmony_ci default_mode.hdisplay, default_mode.vdisplay, 2558c2ecf20Sopenharmony_ci drm_mode_vrefresh(&default_mode)); 2568c2ecf20Sopenharmony_ci return -ENOMEM; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci drm_mode_set_name(mode); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 2628c2ecf20Sopenharmony_ci connector->display_info.width_mm = mode->width_mm; 2638c2ecf20Sopenharmony_ci connector->display_info.height_mm = mode->height_mm; 2648c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 1; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic const struct drm_panel_funcs xpp055c272_funcs = { 2708c2ecf20Sopenharmony_ci .unprepare = xpp055c272_unprepare, 2718c2ecf20Sopenharmony_ci .prepare = xpp055c272_prepare, 2728c2ecf20Sopenharmony_ci .get_modes = xpp055c272_get_modes, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int xpp055c272_probe(struct mipi_dsi_device *dsi) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct device *dev = &dsi->dev; 2788c2ecf20Sopenharmony_ci struct xpp055c272 *ctx; 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 2828c2ecf20Sopenharmony_ci if (!ctx) 2838c2ecf20Sopenharmony_ci return -ENOMEM; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 2868c2ecf20Sopenharmony_ci if (IS_ERR(ctx->reset_gpio)) { 2878c2ecf20Sopenharmony_ci dev_err(dev, "cannot get reset gpio\n"); 2888c2ecf20Sopenharmony_ci return PTR_ERR(ctx->reset_gpio); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ctx->vci = devm_regulator_get(dev, "vci"); 2928c2ecf20Sopenharmony_ci if (IS_ERR(ctx->vci)) { 2938c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->vci); 2948c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 2958c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request vci regulator: %d\n", ret); 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ctx->iovcc = devm_regulator_get(dev, "iovcc"); 3008c2ecf20Sopenharmony_ci if (IS_ERR(ctx->iovcc)) { 3018c2ecf20Sopenharmony_ci ret = PTR_ERR(ctx->iovcc); 3028c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 3038c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request iovcc regulator: %d\n", ret); 3048c2ecf20Sopenharmony_ci return ret; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci mipi_dsi_set_drvdata(dsi, ctx); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ctx->dev = dev; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci dsi->lanes = 4; 3128c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 3138c2ecf20Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 3148c2ecf20Sopenharmony_ci MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs, 3178c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DSI); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ret = drm_panel_of_backlight(&ctx->panel); 3208c2ecf20Sopenharmony_ci if (ret) 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci drm_panel_add(&ctx->panel); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci ret = mipi_dsi_attach(dsi); 3268c2ecf20Sopenharmony_ci if (ret < 0) { 3278c2ecf20Sopenharmony_ci dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); 3288c2ecf20Sopenharmony_ci drm_panel_remove(&ctx->panel); 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic void xpp055c272_shutdown(struct mipi_dsi_device *dsi) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); 3388c2ecf20Sopenharmony_ci int ret; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret = drm_panel_unprepare(&ctx->panel); 3418c2ecf20Sopenharmony_ci if (ret < 0) 3428c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ret = drm_panel_disable(&ctx->panel); 3458c2ecf20Sopenharmony_ci if (ret < 0) 3468c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int xpp055c272_remove(struct mipi_dsi_device *dsi) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); 3528c2ecf20Sopenharmony_ci int ret; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci xpp055c272_shutdown(dsi); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = mipi_dsi_detach(dsi); 3578c2ecf20Sopenharmony_ci if (ret < 0) 3588c2ecf20Sopenharmony_ci dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci drm_panel_remove(&ctx->panel); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic const struct of_device_id xpp055c272_of_match[] = { 3668c2ecf20Sopenharmony_ci { .compatible = "xinpeng,xpp055c272" }, 3678c2ecf20Sopenharmony_ci { /* sentinel */ } 3688c2ecf20Sopenharmony_ci}; 3698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xpp055c272_of_match); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic struct mipi_dsi_driver xpp055c272_driver = { 3728c2ecf20Sopenharmony_ci .driver = { 3738c2ecf20Sopenharmony_ci .name = "panel-xinpeng-xpp055c272", 3748c2ecf20Sopenharmony_ci .of_match_table = xpp055c272_of_match, 3758c2ecf20Sopenharmony_ci }, 3768c2ecf20Sopenharmony_ci .probe = xpp055c272_probe, 3778c2ecf20Sopenharmony_ci .remove = xpp055c272_remove, 3788c2ecf20Sopenharmony_ci .shutdown = xpp055c272_shutdown, 3798c2ecf20Sopenharmony_ci}; 3808c2ecf20Sopenharmony_cimodule_mipi_dsi_driver(xpp055c272_driver); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 3838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel"); 3848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 385