18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Marek Vasut <marex@denx.de> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on tc358764.c by 68c2ecf20Sopenharmony_ci * Andrzej Hajda <a.hajda@samsung.com> 78c2ecf20Sopenharmony_ci * Maciej Purski <m.purski@samsung.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on rpi_touchscreen.c by 108c2ecf20Sopenharmony_ci * Eric Anholt <eric@anholt.net> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <video/mipi_display.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dsi.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 278c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* PPI layer registers */ 308c2ecf20Sopenharmony_ci#define PPI_STARTPPI 0x0104 /* START control bit */ 318c2ecf20Sopenharmony_ci#define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */ 328c2ecf20Sopenharmony_ci#define PPI_D0S_ATMR 0x0144 338c2ecf20Sopenharmony_ci#define PPI_D1S_ATMR 0x0148 348c2ecf20Sopenharmony_ci#define PPI_D0S_CLRSIPOCOUNT 0x0164 /* Assertion timer for Lane 0 */ 358c2ecf20Sopenharmony_ci#define PPI_D1S_CLRSIPOCOUNT 0x0168 /* Assertion timer for Lane 1 */ 368c2ecf20Sopenharmony_ci#define PPI_START_FUNCTION 1 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* DSI layer registers */ 398c2ecf20Sopenharmony_ci#define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */ 408c2ecf20Sopenharmony_ci#define DSI_LANEENABLE 0x0210 /* Enables each lane */ 418c2ecf20Sopenharmony_ci#define DSI_RX_START 1 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* LCDC/DPI Host Registers */ 448c2ecf20Sopenharmony_ci#define LCDCTRL 0x0420 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* SPI Master Registers */ 478c2ecf20Sopenharmony_ci#define SPICMR 0x0450 488c2ecf20Sopenharmony_ci#define SPITCR 0x0454 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* System Controller Registers */ 518c2ecf20Sopenharmony_ci#define SYSCTRL 0x0464 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* System registers */ 548c2ecf20Sopenharmony_ci#define LPX_PERIOD 3 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* Lane enable PPI and DSI register bits */ 578c2ecf20Sopenharmony_ci#define LANEENABLE_CLEN BIT(0) 588c2ecf20Sopenharmony_ci#define LANEENABLE_L0EN BIT(1) 598c2ecf20Sopenharmony_ci#define LANEENABLE_L1EN BIT(2) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct tc358762 { 628c2ecf20Sopenharmony_ci struct device *dev; 638c2ecf20Sopenharmony_ci struct drm_bridge bridge; 648c2ecf20Sopenharmony_ci struct drm_connector connector; 658c2ecf20Sopenharmony_ci struct regulator *regulator; 668c2ecf20Sopenharmony_ci struct drm_bridge *panel_bridge; 678c2ecf20Sopenharmony_ci bool pre_enabled; 688c2ecf20Sopenharmony_ci int error; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int tc358762_clear_error(struct tc358762 *ctx) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int ret = ctx->error; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci ctx->error = 0; 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void tc358762_write(struct tc358762 *ctx, u16 addr, u32 val) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 828c2ecf20Sopenharmony_ci ssize_t ret; 838c2ecf20Sopenharmony_ci u8 data[6]; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (ctx->error) 868c2ecf20Sopenharmony_ci return; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci data[0] = addr; 898c2ecf20Sopenharmony_ci data[1] = addr >> 8; 908c2ecf20Sopenharmony_ci data[2] = val; 918c2ecf20Sopenharmony_ci data[3] = val >> 8; 928c2ecf20Sopenharmony_ci data[4] = val >> 16; 938c2ecf20Sopenharmony_ci data[5] = val >> 24; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ret = mipi_dsi_generic_write(dsi, data, sizeof(data)); 968c2ecf20Sopenharmony_ci if (ret < 0) 978c2ecf20Sopenharmony_ci ctx->error = ret; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline struct tc358762 *bridge_to_tc358762(struct drm_bridge *bridge) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci return container_of(bridge, struct tc358762, bridge); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int tc358762_init(struct tc358762 *ctx) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci tc358762_write(ctx, DSI_LANEENABLE, 1088c2ecf20Sopenharmony_ci LANEENABLE_L0EN | LANEENABLE_CLEN); 1098c2ecf20Sopenharmony_ci tc358762_write(ctx, PPI_D0S_CLRSIPOCOUNT, 5); 1108c2ecf20Sopenharmony_ci tc358762_write(ctx, PPI_D1S_CLRSIPOCOUNT, 5); 1118c2ecf20Sopenharmony_ci tc358762_write(ctx, PPI_D0S_ATMR, 0); 1128c2ecf20Sopenharmony_ci tc358762_write(ctx, PPI_D1S_ATMR, 0); 1138c2ecf20Sopenharmony_ci tc358762_write(ctx, PPI_LPTXTIMECNT, LPX_PERIOD); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci tc358762_write(ctx, SPICMR, 0x00); 1168c2ecf20Sopenharmony_ci tc358762_write(ctx, LCDCTRL, 0x00100150); 1178c2ecf20Sopenharmony_ci tc358762_write(ctx, SYSCTRL, 0x040f); 1188c2ecf20Sopenharmony_ci msleep(100); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci tc358762_write(ctx, PPI_STARTPPI, PPI_START_FUNCTION); 1218c2ecf20Sopenharmony_ci tc358762_write(ctx, DSI_STARTDSI, DSI_RX_START); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci msleep(100); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return tc358762_clear_error(ctx); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void tc358762_post_disable(struct drm_bridge *bridge) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct tc358762 *ctx = bridge_to_tc358762(bridge); 1318c2ecf20Sopenharmony_ci int ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* 1348c2ecf20Sopenharmony_ci * The post_disable hook might be called multiple times. 1358c2ecf20Sopenharmony_ci * We want to avoid regulator imbalance below. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci if (!ctx->pre_enabled) 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci ctx->pre_enabled = false; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ret = regulator_disable(ctx->regulator); 1438c2ecf20Sopenharmony_ci if (ret < 0) 1448c2ecf20Sopenharmony_ci dev_err(ctx->dev, "error disabling regulators (%d)\n", ret); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void tc358762_pre_enable(struct drm_bridge *bridge) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct tc358762 *ctx = bridge_to_tc358762(bridge); 1508c2ecf20Sopenharmony_ci int ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = regulator_enable(ctx->regulator); 1538c2ecf20Sopenharmony_ci if (ret < 0) 1548c2ecf20Sopenharmony_ci dev_err(ctx->dev, "error enabling regulators (%d)\n", ret); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = tc358762_init(ctx); 1578c2ecf20Sopenharmony_ci if (ret < 0) 1588c2ecf20Sopenharmony_ci dev_err(ctx->dev, "error initializing bridge (%d)\n", ret); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ctx->pre_enabled = true; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int tc358762_attach(struct drm_bridge *bridge, 1648c2ecf20Sopenharmony_ci enum drm_bridge_attach_flags flags) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct tc358762 *ctx = bridge_to_tc358762(bridge); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return drm_bridge_attach(bridge->encoder, ctx->panel_bridge, 1698c2ecf20Sopenharmony_ci bridge, flags); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct drm_bridge_funcs tc358762_bridge_funcs = { 1738c2ecf20Sopenharmony_ci .post_disable = tc358762_post_disable, 1748c2ecf20Sopenharmony_ci .pre_enable = tc358762_pre_enable, 1758c2ecf20Sopenharmony_ci .attach = tc358762_attach, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int tc358762_parse_dt(struct tc358762 *ctx) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct drm_bridge *panel_bridge; 1818c2ecf20Sopenharmony_ci struct device *dev = ctx->dev; 1828c2ecf20Sopenharmony_ci struct drm_panel *panel; 1838c2ecf20Sopenharmony_ci int ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL); 1868c2ecf20Sopenharmony_ci if (ret) 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci panel_bridge = devm_drm_panel_bridge_add(dev, panel); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (IS_ERR(panel_bridge)) 1928c2ecf20Sopenharmony_ci return PTR_ERR(panel_bridge); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ctx->panel_bridge = panel_bridge; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int tc358762_configure_regulators(struct tc358762 *ctx) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci ctx->regulator = devm_regulator_get(ctx->dev, "vddc"); 2028c2ecf20Sopenharmony_ci if (IS_ERR(ctx->regulator)) 2038c2ecf20Sopenharmony_ci return PTR_ERR(ctx->regulator); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int tc358762_probe(struct mipi_dsi_device *dsi) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct device *dev = &dsi->dev; 2118c2ecf20Sopenharmony_ci struct tc358762 *ctx; 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(struct tc358762), GFP_KERNEL); 2158c2ecf20Sopenharmony_ci if (!ctx) 2168c2ecf20Sopenharmony_ci return -ENOMEM; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci mipi_dsi_set_drvdata(dsi, ctx); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ctx->dev = dev; 2218c2ecf20Sopenharmony_ci ctx->pre_enabled = false; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* TODO: Find out how to get dual-lane mode working */ 2248c2ecf20Sopenharmony_ci dsi->lanes = 1; 2258c2ecf20Sopenharmony_ci dsi->format = MIPI_DSI_FMT_RGB888; 2268c2ecf20Sopenharmony_ci dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | 2278c2ecf20Sopenharmony_ci MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO_HSE; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ret = tc358762_parse_dt(ctx); 2308c2ecf20Sopenharmony_ci if (ret < 0) 2318c2ecf20Sopenharmony_ci return ret; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ret = tc358762_configure_regulators(ctx); 2348c2ecf20Sopenharmony_ci if (ret < 0) 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci ctx->bridge.funcs = &tc358762_bridge_funcs; 2388c2ecf20Sopenharmony_ci ctx->bridge.type = DRM_MODE_CONNECTOR_DPI; 2398c2ecf20Sopenharmony_ci ctx->bridge.of_node = dev->of_node; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci drm_bridge_add(&ctx->bridge); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ret = mipi_dsi_attach(dsi); 2448c2ecf20Sopenharmony_ci if (ret < 0) { 2458c2ecf20Sopenharmony_ci drm_bridge_remove(&ctx->bridge); 2468c2ecf20Sopenharmony_ci dev_err(dev, "failed to attach dsi\n"); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int tc358762_remove(struct mipi_dsi_device *dsi) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci mipi_dsi_detach(dsi); 2578c2ecf20Sopenharmony_ci drm_bridge_remove(&ctx->bridge); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic const struct of_device_id tc358762_of_match[] = { 2638c2ecf20Sopenharmony_ci { .compatible = "toshiba,tc358762" }, 2648c2ecf20Sopenharmony_ci { } 2658c2ecf20Sopenharmony_ci}; 2668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tc358762_of_match); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic struct mipi_dsi_driver tc358762_driver = { 2698c2ecf20Sopenharmony_ci .probe = tc358762_probe, 2708c2ecf20Sopenharmony_ci .remove = tc358762_remove, 2718c2ecf20Sopenharmony_ci .driver = { 2728c2ecf20Sopenharmony_ci .name = "tc358762", 2738c2ecf20Sopenharmony_ci .of_match_table = tc358762_of_match, 2748c2ecf20Sopenharmony_ci }, 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_cimodule_mipi_dsi_driver(tc358762_driver); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 2798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MIPI-DSI based Driver for TC358762 DSI/DPI Bridge"); 2808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 281