18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 48c2ecf20Sopenharmony_ci * Author: 58c2ecf20Sopenharmony_ci * Mark Yao <mark.yao@rock-chips.com> 68c2ecf20Sopenharmony_ci * Sandy Huang <hjc@rock-chips.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/component.h> 118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 128c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 138c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 148c2ecf20Sopenharmony_ci#include <linux/pinctrl/devinfo.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <linux/reset.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "rockchip_drm_drv.h" 298c2ecf20Sopenharmony_ci#include "rockchip_drm_vop.h" 308c2ecf20Sopenharmony_ci#include "rockchip_lvds.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define DISPLAY_OUTPUT_RGB 0 338c2ecf20Sopenharmony_ci#define DISPLAY_OUTPUT_LVDS 1 348c2ecf20Sopenharmony_ci#define DISPLAY_OUTPUT_DUAL_LVDS 2 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct rockchip_lvds; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define connector_to_lvds(c) \ 398c2ecf20Sopenharmony_ci container_of(c, struct rockchip_lvds, connector) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define encoder_to_lvds(c) \ 428c2ecf20Sopenharmony_ci container_of(c, struct rockchip_lvds, encoder) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/** 458c2ecf20Sopenharmony_ci * rockchip_lvds_soc_data - rockchip lvds Soc private data 468c2ecf20Sopenharmony_ci * @probe: LVDS platform probe function 478c2ecf20Sopenharmony_ci * @helper_funcs: LVDS connector helper functions 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistruct rockchip_lvds_soc_data { 508c2ecf20Sopenharmony_ci int (*probe)(struct platform_device *pdev, struct rockchip_lvds *lvds); 518c2ecf20Sopenharmony_ci const struct drm_encoder_helper_funcs *helper_funcs; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct rockchip_lvds { 558c2ecf20Sopenharmony_ci struct device *dev; 568c2ecf20Sopenharmony_ci void __iomem *regs; 578c2ecf20Sopenharmony_ci struct regmap *grf; 588c2ecf20Sopenharmony_ci struct clk *pclk; 598c2ecf20Sopenharmony_ci struct phy *dphy; 608c2ecf20Sopenharmony_ci const struct rockchip_lvds_soc_data *soc_data; 618c2ecf20Sopenharmony_ci int output; /* rgb lvds or dual lvds output */ 628c2ecf20Sopenharmony_ci int format; /* vesa or jeida format */ 638c2ecf20Sopenharmony_ci struct drm_device *drm_dev; 648c2ecf20Sopenharmony_ci struct drm_panel *panel; 658c2ecf20Sopenharmony_ci struct drm_bridge *bridge; 668c2ecf20Sopenharmony_ci struct drm_connector connector; 678c2ecf20Sopenharmony_ci struct drm_encoder encoder; 688c2ecf20Sopenharmony_ci struct dev_pin_info *pins; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset, 728c2ecf20Sopenharmony_ci u32 val) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci writel_relaxed(val, lvds->regs + offset); 758c2ecf20Sopenharmony_ci if (lvds->output == DISPLAY_OUTPUT_LVDS) 768c2ecf20Sopenharmony_ci return; 778c2ecf20Sopenharmony_ci writel_relaxed(val, lvds->regs + offset + RK3288_LVDS_CH1_OFFSET); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline int rockchip_lvds_name_to_format(const char *s) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci if (strncmp(s, "jeida-18", 8) == 0) 838c2ecf20Sopenharmony_ci return LVDS_JEIDA_18; 848c2ecf20Sopenharmony_ci else if (strncmp(s, "jeida-24", 8) == 0) 858c2ecf20Sopenharmony_ci return LVDS_JEIDA_24; 868c2ecf20Sopenharmony_ci else if (strncmp(s, "vesa-24", 7) == 0) 878c2ecf20Sopenharmony_ci return LVDS_VESA_24; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return -EINVAL; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline int rockchip_lvds_name_to_output(const char *s) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci if (strncmp(s, "rgb", 3) == 0) 958c2ecf20Sopenharmony_ci return DISPLAY_OUTPUT_RGB; 968c2ecf20Sopenharmony_ci else if (strncmp(s, "lvds", 4) == 0) 978c2ecf20Sopenharmony_ci return DISPLAY_OUTPUT_LVDS; 988c2ecf20Sopenharmony_ci else if (strncmp(s, "duallvds", 8) == 0) 998c2ecf20Sopenharmony_ci return DISPLAY_OUTPUT_DUAL_LVDS; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs rockchip_lvds_connector_funcs = { 1058c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 1068c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 1078c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 1088c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 1098c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int rockchip_lvds_connector_get_modes(struct drm_connector *connector) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = connector_to_lvds(connector); 1158c2ecf20Sopenharmony_ci struct drm_panel *panel = lvds->panel; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return drm_panel_get_modes(panel, connector); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic const 1218c2ecf20Sopenharmony_cistruct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = { 1228c2ecf20Sopenharmony_ci .get_modes = rockchip_lvds_connector_get_modes, 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int 1268c2ecf20Sopenharmony_cirockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder, 1278c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 1288c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci s->output_mode = ROCKCHIP_OUT_MODE_P888; 1338c2ecf20Sopenharmony_ci s->output_type = DRM_MODE_CONNECTOR_LVDS; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int rk3288_lvds_poweron(struct rockchip_lvds *lvds) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci u32 val; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ret = clk_enable(lvds->pclk); 1448c2ecf20Sopenharmony_ci if (ret < 0) { 1458c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret); 1468c2ecf20Sopenharmony_ci return ret; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(lvds->dev); 1498c2ecf20Sopenharmony_ci if (ret < 0) { 1508c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret); 1518c2ecf20Sopenharmony_ci clk_disable(lvds->pclk); 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci val = RK3288_LVDS_CH0_REG0_LANE4_EN | RK3288_LVDS_CH0_REG0_LANE3_EN | 1558c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG0_LANE2_EN | RK3288_LVDS_CH0_REG0_LANE1_EN | 1568c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG0_LANE0_EN; 1578c2ecf20Sopenharmony_ci if (lvds->output == DISPLAY_OUTPUT_RGB) { 1588c2ecf20Sopenharmony_ci val |= RK3288_LVDS_CH0_REG0_TTL_EN | 1598c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG0_LANECK_EN; 1608c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val); 1618c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG2, 1628c2ecf20Sopenharmony_ci RK3288_LVDS_PLL_FBDIV_REG2(0x46)); 1638c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, 1648c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE | 1658c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE | 1668c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE | 1678c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE | 1688c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE | 1698c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE); 1708c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, 1718c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA | 1728c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA | 1738c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA | 1748c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA | 1758c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA | 1768c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA); 1778c2ecf20Sopenharmony_ci } else { 1788c2ecf20Sopenharmony_ci val |= RK3288_LVDS_CH0_REG0_LVDS_EN | 1798c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG0_LANECK_EN; 1808c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val); 1818c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG1, 1828c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANECK_BIAS | 1838c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE4_BIAS | 1848c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE3_BIAS | 1858c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE2_BIAS | 1868c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE1_BIAS | 1878c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE0_BIAS); 1888c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG2, 1898c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG2_RESERVE_ON | 1908c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE | 1918c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE | 1928c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE | 1938c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE | 1948c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE | 1958c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE | 1968c2ecf20Sopenharmony_ci RK3288_LVDS_PLL_FBDIV_REG2(0x46)); 1978c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00); 1988c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG3, 2018c2ecf20Sopenharmony_ci RK3288_LVDS_PLL_FBDIV_REG3(0x46)); 2028c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REGD, 2038c2ecf20Sopenharmony_ci RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); 2048c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG20, 2058c2ecf20Sopenharmony_ci RK3288_LVDS_CH0_REG20_LSB); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CFG_REGC, 2088c2ecf20Sopenharmony_ci RK3288_LVDS_CFG_REGC_PLL_ENABLE); 2098c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CFG_REG21, 2108c2ecf20Sopenharmony_ci RK3288_LVDS_CFG_REG21_TX_ENABLE); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void rk3288_lvds_poweroff(struct rockchip_lvds *lvds) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci int ret; 2188c2ecf20Sopenharmony_ci u32 val; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CFG_REG21, 2218c2ecf20Sopenharmony_ci RK3288_LVDS_CFG_REG21_TX_ENABLE); 2228c2ecf20Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CFG_REGC, 2238c2ecf20Sopenharmony_ci RK3288_LVDS_CFG_REGC_PLL_ENABLE); 2248c2ecf20Sopenharmony_ci val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN; 2258c2ecf20Sopenharmony_ci val |= val << 16; 2268c2ecf20Sopenharmony_ci ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val); 2278c2ecf20Sopenharmony_ci if (ret != 0) 2288c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci pm_runtime_put(lvds->dev); 2318c2ecf20Sopenharmony_ci clk_disable(lvds->pclk); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int rk3288_lvds_grf_config(struct drm_encoder *encoder, 2358c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 2388c2ecf20Sopenharmony_ci u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0; 2398c2ecf20Sopenharmony_ci u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0; 2408c2ecf20Sopenharmony_ci u32 val; 2418c2ecf20Sopenharmony_ci int ret; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* iomux to LCD data/sync mode */ 2448c2ecf20Sopenharmony_ci if (lvds->output == DISPLAY_OUTPUT_RGB) 2458c2ecf20Sopenharmony_ci if (lvds->pins && !IS_ERR(lvds->pins->default_state)) 2468c2ecf20Sopenharmony_ci pinctrl_select_state(lvds->pins->p, 2478c2ecf20Sopenharmony_ci lvds->pins->default_state); 2488c2ecf20Sopenharmony_ci val = lvds->format | LVDS_CH0_EN; 2498c2ecf20Sopenharmony_ci if (lvds->output == DISPLAY_OUTPUT_RGB) 2508c2ecf20Sopenharmony_ci val |= LVDS_TTL_EN | LVDS_CH1_EN; 2518c2ecf20Sopenharmony_ci else if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS) 2528c2ecf20Sopenharmony_ci val |= LVDS_DUAL | LVDS_CH1_EN; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if ((mode->htotal - mode->hsync_start) & 0x01) 2558c2ecf20Sopenharmony_ci val |= LVDS_START_PHASE_RST_1; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci val |= (pin_dclk << 8) | (pin_hsync << 9); 2588c2ecf20Sopenharmony_ci val |= (0xffff << 16); 2598c2ecf20Sopenharmony_ci ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val); 2608c2ecf20Sopenharmony_ci if (ret) 2618c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return ret; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int rk3288_lvds_set_vop_source(struct rockchip_lvds *lvds, 2678c2ecf20Sopenharmony_ci struct drm_encoder *encoder) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci u32 val; 2708c2ecf20Sopenharmony_ci int ret; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder); 2738c2ecf20Sopenharmony_ci if (ret < 0) 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16; 2778c2ecf20Sopenharmony_ci if (ret) 2788c2ecf20Sopenharmony_ci val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON6, val); 2818c2ecf20Sopenharmony_ci if (ret < 0) 2828c2ecf20Sopenharmony_ci return ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic void rk3288_lvds_encoder_enable(struct drm_encoder *encoder) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 2908c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; 2918c2ecf20Sopenharmony_ci int ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci drm_panel_prepare(lvds->panel); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ret = rk3288_lvds_poweron(lvds); 2968c2ecf20Sopenharmony_ci if (ret < 0) { 2978c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret); 2988c2ecf20Sopenharmony_ci drm_panel_unprepare(lvds->panel); 2998c2ecf20Sopenharmony_ci return; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = rk3288_lvds_grf_config(encoder, mode); 3038c2ecf20Sopenharmony_ci if (ret) { 3048c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret); 3058c2ecf20Sopenharmony_ci drm_panel_unprepare(lvds->panel); 3068c2ecf20Sopenharmony_ci return; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = rk3288_lvds_set_vop_source(lvds, encoder); 3108c2ecf20Sopenharmony_ci if (ret) { 3118c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n", ret); 3128c2ecf20Sopenharmony_ci drm_panel_unprepare(lvds->panel); 3138c2ecf20Sopenharmony_ci return; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci drm_panel_enable(lvds->panel); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void rk3288_lvds_encoder_disable(struct drm_encoder *encoder) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci drm_panel_disable(lvds->panel); 3248c2ecf20Sopenharmony_ci rk3288_lvds_poweroff(lvds); 3258c2ecf20Sopenharmony_ci drm_panel_unprepare(lvds->panel); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int px30_lvds_poweron(struct rockchip_lvds *lvds) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int ret; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(lvds->dev); 3338c2ecf20Sopenharmony_ci if (ret < 0) { 3348c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret); 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Enable LVDS mode */ 3398c2ecf20Sopenharmony_ci ret = regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 3408c2ecf20Sopenharmony_ci PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1), 3418c2ecf20Sopenharmony_ci PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1)); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci pm_runtime_put(lvds->dev); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic void px30_lvds_poweroff(struct rockchip_lvds *lvds) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 3518c2ecf20Sopenharmony_ci PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1), 3528c2ecf20Sopenharmony_ci PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0)); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci pm_runtime_put(lvds->dev); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int px30_lvds_grf_config(struct drm_encoder *encoder, 3588c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (lvds->output != DISPLAY_OUTPUT_LVDS) { 3638c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "Unsupported display output %d\n", 3648c2ecf20Sopenharmony_ci lvds->output); 3658c2ecf20Sopenharmony_ci return -EINVAL; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Set format */ 3698c2ecf20Sopenharmony_ci return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 3708c2ecf20Sopenharmony_ci PX30_LVDS_FORMAT(lvds->format), 3718c2ecf20Sopenharmony_ci PX30_LVDS_FORMAT(lvds->format)); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int px30_lvds_set_vop_source(struct rockchip_lvds *lvds, 3758c2ecf20Sopenharmony_ci struct drm_encoder *encoder) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int vop; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci vop = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder); 3808c2ecf20Sopenharmony_ci if (vop < 0) 3818c2ecf20Sopenharmony_ci return vop; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 3848c2ecf20Sopenharmony_ci PX30_LVDS_VOP_SEL(1), 3858c2ecf20Sopenharmony_ci PX30_LVDS_VOP_SEL(vop)); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void px30_lvds_encoder_enable(struct drm_encoder *encoder) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 3918c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; 3928c2ecf20Sopenharmony_ci int ret; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci drm_panel_prepare(lvds->panel); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ret = px30_lvds_poweron(lvds); 3978c2ecf20Sopenharmony_ci if (ret) { 3988c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret); 3998c2ecf20Sopenharmony_ci drm_panel_unprepare(lvds->panel); 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ret = px30_lvds_grf_config(encoder, mode); 4048c2ecf20Sopenharmony_ci if (ret) { 4058c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret); 4068c2ecf20Sopenharmony_ci drm_panel_unprepare(lvds->panel); 4078c2ecf20Sopenharmony_ci return; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci ret = px30_lvds_set_vop_source(lvds, encoder); 4118c2ecf20Sopenharmony_ci if (ret) { 4128c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n", ret); 4138c2ecf20Sopenharmony_ci drm_panel_unprepare(lvds->panel); 4148c2ecf20Sopenharmony_ci return; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci drm_panel_enable(lvds->panel); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic void px30_lvds_encoder_disable(struct drm_encoder *encoder) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci drm_panel_disable(lvds->panel); 4258c2ecf20Sopenharmony_ci px30_lvds_poweroff(lvds); 4268c2ecf20Sopenharmony_ci drm_panel_unprepare(lvds->panel); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic const 4308c2ecf20Sopenharmony_cistruct drm_encoder_helper_funcs rk3288_lvds_encoder_helper_funcs = { 4318c2ecf20Sopenharmony_ci .enable = rk3288_lvds_encoder_enable, 4328c2ecf20Sopenharmony_ci .disable = rk3288_lvds_encoder_disable, 4338c2ecf20Sopenharmony_ci .atomic_check = rockchip_lvds_encoder_atomic_check, 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic const 4378c2ecf20Sopenharmony_cistruct drm_encoder_helper_funcs px30_lvds_encoder_helper_funcs = { 4388c2ecf20Sopenharmony_ci .enable = px30_lvds_encoder_enable, 4398c2ecf20Sopenharmony_ci .disable = px30_lvds_encoder_disable, 4408c2ecf20Sopenharmony_ci .atomic_check = rockchip_lvds_encoder_atomic_check, 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int rk3288_lvds_probe(struct platform_device *pdev, 4448c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct resource *res; 4478c2ecf20Sopenharmony_ci int ret; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4508c2ecf20Sopenharmony_ci lvds->regs = devm_ioremap_resource(lvds->dev, res); 4518c2ecf20Sopenharmony_ci if (IS_ERR(lvds->regs)) 4528c2ecf20Sopenharmony_ci return PTR_ERR(lvds->regs); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci lvds->pclk = devm_clk_get(lvds->dev, "pclk_lvds"); 4558c2ecf20Sopenharmony_ci if (IS_ERR(lvds->pclk)) { 4568c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "could not get pclk_lvds\n"); 4578c2ecf20Sopenharmony_ci return PTR_ERR(lvds->pclk); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins), 4618c2ecf20Sopenharmony_ci GFP_KERNEL); 4628c2ecf20Sopenharmony_ci if (!lvds->pins) 4638c2ecf20Sopenharmony_ci return -ENOMEM; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci lvds->pins->p = devm_pinctrl_get(lvds->dev); 4668c2ecf20Sopenharmony_ci if (IS_ERR(lvds->pins->p)) { 4678c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "no pinctrl handle\n"); 4688c2ecf20Sopenharmony_ci devm_kfree(lvds->dev, lvds->pins); 4698c2ecf20Sopenharmony_ci lvds->pins = NULL; 4708c2ecf20Sopenharmony_ci } else { 4718c2ecf20Sopenharmony_ci lvds->pins->default_state = 4728c2ecf20Sopenharmony_ci pinctrl_lookup_state(lvds->pins->p, "lcdc"); 4738c2ecf20Sopenharmony_ci if (IS_ERR(lvds->pins->default_state)) { 4748c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "no default pinctrl state\n"); 4758c2ecf20Sopenharmony_ci devm_kfree(lvds->dev, lvds->pins); 4768c2ecf20Sopenharmony_ci lvds->pins = NULL; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ret = clk_prepare(lvds->pclk); 4818c2ecf20Sopenharmony_ci if (ret < 0) { 4828c2ecf20Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to prepare pclk_lvds\n"); 4838c2ecf20Sopenharmony_ci return ret; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int px30_lvds_probe(struct platform_device *pdev, 4908c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci int ret; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* MSB */ 4958c2ecf20Sopenharmony_ci ret = regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 4968c2ecf20Sopenharmony_ci PX30_LVDS_MSBSEL(1), 4978c2ecf20Sopenharmony_ci PX30_LVDS_MSBSEL(1)); 4988c2ecf20Sopenharmony_ci if (ret) 4998c2ecf20Sopenharmony_ci return ret; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* PHY */ 5028c2ecf20Sopenharmony_ci lvds->dphy = devm_phy_get(&pdev->dev, "dphy"); 5038c2ecf20Sopenharmony_ci if (IS_ERR(lvds->dphy)) 5048c2ecf20Sopenharmony_ci return PTR_ERR(lvds->dphy); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci ret = phy_init(lvds->dphy); 5078c2ecf20Sopenharmony_ci if (ret) 5088c2ecf20Sopenharmony_ci return ret; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ret = phy_set_mode(lvds->dphy, PHY_MODE_LVDS); 5118c2ecf20Sopenharmony_ci if (ret) 5128c2ecf20Sopenharmony_ci return ret; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci return phy_power_on(lvds->dphy); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic const struct rockchip_lvds_soc_data rk3288_lvds_data = { 5188c2ecf20Sopenharmony_ci .probe = rk3288_lvds_probe, 5198c2ecf20Sopenharmony_ci .helper_funcs = &rk3288_lvds_encoder_helper_funcs, 5208c2ecf20Sopenharmony_ci}; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic const struct rockchip_lvds_soc_data px30_lvds_data = { 5238c2ecf20Sopenharmony_ci .probe = px30_lvds_probe, 5248c2ecf20Sopenharmony_ci .helper_funcs = &px30_lvds_encoder_helper_funcs, 5258c2ecf20Sopenharmony_ci}; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic const struct of_device_id rockchip_lvds_dt_ids[] = { 5288c2ecf20Sopenharmony_ci { 5298c2ecf20Sopenharmony_ci .compatible = "rockchip,rk3288-lvds", 5308c2ecf20Sopenharmony_ci .data = &rk3288_lvds_data 5318c2ecf20Sopenharmony_ci }, 5328c2ecf20Sopenharmony_ci { 5338c2ecf20Sopenharmony_ci .compatible = "rockchip,px30-lvds", 5348c2ecf20Sopenharmony_ci .data = &px30_lvds_data 5358c2ecf20Sopenharmony_ci }, 5368c2ecf20Sopenharmony_ci {} 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int rockchip_lvds_bind(struct device *dev, struct device *master, 5418c2ecf20Sopenharmony_ci void *data) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = dev_get_drvdata(dev); 5448c2ecf20Sopenharmony_ci struct drm_device *drm_dev = data; 5458c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 5468c2ecf20Sopenharmony_ci struct drm_connector *connector; 5478c2ecf20Sopenharmony_ci struct device_node *remote = NULL; 5488c2ecf20Sopenharmony_ci struct device_node *port, *endpoint; 5498c2ecf20Sopenharmony_ci int ret = 0, child_count = 0; 5508c2ecf20Sopenharmony_ci const char *name; 5518c2ecf20Sopenharmony_ci u32 endpoint_id = 0; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci lvds->drm_dev = drm_dev; 5548c2ecf20Sopenharmony_ci port = of_graph_get_port_by_id(dev->of_node, 1); 5558c2ecf20Sopenharmony_ci if (!port) { 5568c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, 5578c2ecf20Sopenharmony_ci "can't found port point, please init lvds panel port!\n"); 5588c2ecf20Sopenharmony_ci return -EINVAL; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci for_each_child_of_node(port, endpoint) { 5618c2ecf20Sopenharmony_ci child_count++; 5628c2ecf20Sopenharmony_ci of_property_read_u32(endpoint, "reg", &endpoint_id); 5638c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id, 5648c2ecf20Sopenharmony_ci &lvds->panel, &lvds->bridge); 5658c2ecf20Sopenharmony_ci if (!ret) { 5668c2ecf20Sopenharmony_ci of_node_put(endpoint); 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci if (!child_count) { 5718c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "lvds port does not have any children\n"); 5728c2ecf20Sopenharmony_ci ret = -EINVAL; 5738c2ecf20Sopenharmony_ci goto err_put_port; 5748c2ecf20Sopenharmony_ci } else if (ret) { 5758c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n"); 5768c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 5778c2ecf20Sopenharmony_ci goto err_put_port; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci if (lvds->panel) 5808c2ecf20Sopenharmony_ci remote = lvds->panel->dev->of_node; 5818c2ecf20Sopenharmony_ci else 5828c2ecf20Sopenharmony_ci remote = lvds->bridge->of_node; 5838c2ecf20Sopenharmony_ci if (of_property_read_string(dev->of_node, "rockchip,output", &name)) 5848c2ecf20Sopenharmony_ci /* default set it as output rgb */ 5858c2ecf20Sopenharmony_ci lvds->output = DISPLAY_OUTPUT_RGB; 5868c2ecf20Sopenharmony_ci else 5878c2ecf20Sopenharmony_ci lvds->output = rockchip_lvds_name_to_output(name); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (lvds->output < 0) { 5908c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name); 5918c2ecf20Sopenharmony_ci ret = lvds->output; 5928c2ecf20Sopenharmony_ci goto err_put_remote; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (of_property_read_string(remote, "data-mapping", &name)) 5968c2ecf20Sopenharmony_ci /* default set it as format vesa 18 */ 5978c2ecf20Sopenharmony_ci lvds->format = LVDS_VESA_18; 5988c2ecf20Sopenharmony_ci else 5998c2ecf20Sopenharmony_ci lvds->format = rockchip_lvds_name_to_format(name); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (lvds->format < 0) { 6028c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name); 6038c2ecf20Sopenharmony_ci ret = lvds->format; 6048c2ecf20Sopenharmony_ci goto err_put_remote; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci encoder = &lvds->encoder; 6088c2ecf20Sopenharmony_ci encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, 6098c2ecf20Sopenharmony_ci dev->of_node); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_LVDS); 6128c2ecf20Sopenharmony_ci if (ret < 0) { 6138c2ecf20Sopenharmony_ci DRM_DEV_ERROR(drm_dev->dev, 6148c2ecf20Sopenharmony_ci "failed to initialize encoder: %d\n", ret); 6158c2ecf20Sopenharmony_ci goto err_put_remote; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, lvds->soc_data->helper_funcs); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (lvds->panel) { 6218c2ecf20Sopenharmony_ci connector = &lvds->connector; 6228c2ecf20Sopenharmony_ci connector->dpms = DRM_MODE_DPMS_OFF; 6238c2ecf20Sopenharmony_ci ret = drm_connector_init(drm_dev, connector, 6248c2ecf20Sopenharmony_ci &rockchip_lvds_connector_funcs, 6258c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_LVDS); 6268c2ecf20Sopenharmony_ci if (ret < 0) { 6278c2ecf20Sopenharmony_ci DRM_DEV_ERROR(drm_dev->dev, 6288c2ecf20Sopenharmony_ci "failed to initialize connector: %d\n", ret); 6298c2ecf20Sopenharmony_ci goto err_free_encoder; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, 6338c2ecf20Sopenharmony_ci &rockchip_lvds_connector_helper_funcs); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci ret = drm_connector_attach_encoder(connector, encoder); 6368c2ecf20Sopenharmony_ci if (ret < 0) { 6378c2ecf20Sopenharmony_ci DRM_DEV_ERROR(drm_dev->dev, 6388c2ecf20Sopenharmony_ci "failed to attach encoder: %d\n", ret); 6398c2ecf20Sopenharmony_ci goto err_free_connector; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci } else { 6428c2ecf20Sopenharmony_ci ret = drm_bridge_attach(encoder, lvds->bridge, NULL, 0); 6438c2ecf20Sopenharmony_ci if (ret) { 6448c2ecf20Sopenharmony_ci DRM_DEV_ERROR(drm_dev->dev, 6458c2ecf20Sopenharmony_ci "failed to attach bridge: %d\n", ret); 6468c2ecf20Sopenharmony_ci goto err_free_encoder; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 6518c2ecf20Sopenharmony_ci of_node_put(remote); 6528c2ecf20Sopenharmony_ci of_node_put(port); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cierr_free_connector: 6578c2ecf20Sopenharmony_ci drm_connector_cleanup(connector); 6588c2ecf20Sopenharmony_cierr_free_encoder: 6598c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 6608c2ecf20Sopenharmony_cierr_put_remote: 6618c2ecf20Sopenharmony_ci of_node_put(remote); 6628c2ecf20Sopenharmony_cierr_put_port: 6638c2ecf20Sopenharmony_ci of_node_put(port); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci return ret; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void rockchip_lvds_unbind(struct device *dev, struct device *master, 6698c2ecf20Sopenharmony_ci void *data) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = dev_get_drvdata(dev); 6728c2ecf20Sopenharmony_ci const struct drm_encoder_helper_funcs *encoder_funcs; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci encoder_funcs = lvds->soc_data->helper_funcs; 6758c2ecf20Sopenharmony_ci encoder_funcs->disable(&lvds->encoder); 6768c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 6778c2ecf20Sopenharmony_ci drm_connector_cleanup(&lvds->connector); 6788c2ecf20Sopenharmony_ci drm_encoder_cleanup(&lvds->encoder); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic const struct component_ops rockchip_lvds_component_ops = { 6828c2ecf20Sopenharmony_ci .bind = rockchip_lvds_bind, 6838c2ecf20Sopenharmony_ci .unbind = rockchip_lvds_unbind, 6848c2ecf20Sopenharmony_ci}; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int rockchip_lvds_probe(struct platform_device *pdev) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6898c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds; 6908c2ecf20Sopenharmony_ci const struct of_device_id *match; 6918c2ecf20Sopenharmony_ci int ret; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (!dev->of_node) 6948c2ecf20Sopenharmony_ci return -ENODEV; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); 6978c2ecf20Sopenharmony_ci if (!lvds) 6988c2ecf20Sopenharmony_ci return -ENOMEM; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci lvds->dev = dev; 7018c2ecf20Sopenharmony_ci match = of_match_node(rockchip_lvds_dt_ids, dev->of_node); 7028c2ecf20Sopenharmony_ci if (!match) 7038c2ecf20Sopenharmony_ci return -ENODEV; 7048c2ecf20Sopenharmony_ci lvds->soc_data = match->data; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node, 7078c2ecf20Sopenharmony_ci "rockchip,grf"); 7088c2ecf20Sopenharmony_ci if (IS_ERR(lvds->grf)) { 7098c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "missing rockchip,grf property\n"); 7108c2ecf20Sopenharmony_ci return PTR_ERR(lvds->grf); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci ret = lvds->soc_data->probe(pdev, lvds); 7148c2ecf20Sopenharmony_ci if (ret) { 7158c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Platform initialization failed\n"); 7168c2ecf20Sopenharmony_ci return ret; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci dev_set_drvdata(dev, lvds); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci ret = component_add(&pdev->dev, &rockchip_lvds_component_ops); 7228c2ecf20Sopenharmony_ci if (ret < 0) { 7238c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to add component\n"); 7248c2ecf20Sopenharmony_ci clk_unprepare(lvds->pclk); 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci return ret; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int rockchip_lvds_remove(struct platform_device *pdev) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci component_del(&pdev->dev, &rockchip_lvds_component_ops); 7358c2ecf20Sopenharmony_ci clk_unprepare(lvds->pclk); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci return 0; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistruct platform_driver rockchip_lvds_driver = { 7418c2ecf20Sopenharmony_ci .probe = rockchip_lvds_probe, 7428c2ecf20Sopenharmony_ci .remove = rockchip_lvds_remove, 7438c2ecf20Sopenharmony_ci .driver = { 7448c2ecf20Sopenharmony_ci .name = "rockchip-lvds", 7458c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(rockchip_lvds_dt_ids), 7468c2ecf20Sopenharmony_ci }, 7478c2ecf20Sopenharmony_ci}; 748