162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 462306a36Sopenharmony_ci * Author: 562306a36Sopenharmony_ci * Mark Yao <mark.yao@rock-chips.com> 662306a36Sopenharmony_ci * Sandy Huang <hjc@rock-chips.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/component.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci#include <linux/of_graph.h> 1362306a36Sopenharmony_ci#include <linux/phy/phy.h> 1462306a36Sopenharmony_ci#include <linux/pinctrl/devinfo.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/reset.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <drm/display/drm_dp_helper.h> 2162306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2262306a36Sopenharmony_ci#include <drm/drm_bridge.h> 2362306a36Sopenharmony_ci#include <drm/drm_bridge_connector.h> 2462306a36Sopenharmony_ci#include <drm/drm_of.h> 2562306a36Sopenharmony_ci#include <drm/drm_panel.h> 2662306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2762306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "rockchip_drm_drv.h" 3062306a36Sopenharmony_ci#include "rockchip_drm_vop.h" 3162306a36Sopenharmony_ci#include "rockchip_lvds.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define DISPLAY_OUTPUT_RGB 0 3462306a36Sopenharmony_ci#define DISPLAY_OUTPUT_LVDS 1 3562306a36Sopenharmony_ci#define DISPLAY_OUTPUT_DUAL_LVDS 2 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct rockchip_lvds; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * struct rockchip_lvds_soc_data - rockchip lvds Soc private data 4162306a36Sopenharmony_ci * @probe: LVDS platform probe function 4262306a36Sopenharmony_ci * @helper_funcs: LVDS connector helper functions 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistruct rockchip_lvds_soc_data { 4562306a36Sopenharmony_ci int (*probe)(struct platform_device *pdev, struct rockchip_lvds *lvds); 4662306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *helper_funcs; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct rockchip_lvds { 5062306a36Sopenharmony_ci struct device *dev; 5162306a36Sopenharmony_ci void __iomem *regs; 5262306a36Sopenharmony_ci struct regmap *grf; 5362306a36Sopenharmony_ci struct clk *pclk; 5462306a36Sopenharmony_ci struct phy *dphy; 5562306a36Sopenharmony_ci const struct rockchip_lvds_soc_data *soc_data; 5662306a36Sopenharmony_ci int output; /* rgb lvds or dual lvds output */ 5762306a36Sopenharmony_ci int format; /* vesa or jeida format */ 5862306a36Sopenharmony_ci struct drm_device *drm_dev; 5962306a36Sopenharmony_ci struct drm_panel *panel; 6062306a36Sopenharmony_ci struct drm_bridge *bridge; 6162306a36Sopenharmony_ci struct drm_connector connector; 6262306a36Sopenharmony_ci struct rockchip_encoder encoder; 6362306a36Sopenharmony_ci struct dev_pin_info *pins; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *connector) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci return container_of(connector, struct rockchip_lvds, connector); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *encoder) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return container_of(rkencoder, struct rockchip_lvds, encoder); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset, 7962306a36Sopenharmony_ci u32 val) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci writel_relaxed(val, lvds->regs + offset); 8262306a36Sopenharmony_ci if (lvds->output == DISPLAY_OUTPUT_LVDS) 8362306a36Sopenharmony_ci return; 8462306a36Sopenharmony_ci writel_relaxed(val, lvds->regs + offset + RK3288_LVDS_CH1_OFFSET); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline int rockchip_lvds_name_to_format(const char *s) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci if (strncmp(s, "jeida-18", 8) == 0) 9062306a36Sopenharmony_ci return LVDS_JEIDA_18; 9162306a36Sopenharmony_ci else if (strncmp(s, "jeida-24", 8) == 0) 9262306a36Sopenharmony_ci return LVDS_JEIDA_24; 9362306a36Sopenharmony_ci else if (strncmp(s, "vesa-24", 7) == 0) 9462306a36Sopenharmony_ci return LVDS_VESA_24; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline int rockchip_lvds_name_to_output(const char *s) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci if (strncmp(s, "rgb", 3) == 0) 10262306a36Sopenharmony_ci return DISPLAY_OUTPUT_RGB; 10362306a36Sopenharmony_ci else if (strncmp(s, "lvds", 4) == 0) 10462306a36Sopenharmony_ci return DISPLAY_OUTPUT_LVDS; 10562306a36Sopenharmony_ci else if (strncmp(s, "duallvds", 8) == 0) 10662306a36Sopenharmony_ci return DISPLAY_OUTPUT_DUAL_LVDS; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return -EINVAL; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const struct drm_connector_funcs rockchip_lvds_connector_funcs = { 11262306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 11362306a36Sopenharmony_ci .destroy = drm_connector_cleanup, 11462306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 11562306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 11662306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int rockchip_lvds_connector_get_modes(struct drm_connector *connector) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct rockchip_lvds *lvds = connector_to_lvds(connector); 12262306a36Sopenharmony_ci struct drm_panel *panel = lvds->panel; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return drm_panel_get_modes(panel, connector); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic const 12862306a36Sopenharmony_cistruct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = { 12962306a36Sopenharmony_ci .get_modes = rockchip_lvds_connector_get_modes, 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int 13362306a36Sopenharmony_cirockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder, 13462306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 13562306a36Sopenharmony_ci struct drm_connector_state *conn_state) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci s->output_mode = ROCKCHIP_OUT_MODE_P888; 14062306a36Sopenharmony_ci s->output_type = DRM_MODE_CONNECTOR_LVDS; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int rk3288_lvds_poweron(struct rockchip_lvds *lvds) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int ret; 14862306a36Sopenharmony_ci u32 val; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ret = clk_enable(lvds->pclk); 15162306a36Sopenharmony_ci if (ret < 0) { 15262306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret); 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(lvds->dev); 15662306a36Sopenharmony_ci if (ret < 0) { 15762306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret); 15862306a36Sopenharmony_ci clk_disable(lvds->pclk); 15962306a36Sopenharmony_ci return ret; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci val = RK3288_LVDS_CH0_REG0_LANE4_EN | RK3288_LVDS_CH0_REG0_LANE3_EN | 16262306a36Sopenharmony_ci RK3288_LVDS_CH0_REG0_LANE2_EN | RK3288_LVDS_CH0_REG0_LANE1_EN | 16362306a36Sopenharmony_ci RK3288_LVDS_CH0_REG0_LANE0_EN; 16462306a36Sopenharmony_ci if (lvds->output == DISPLAY_OUTPUT_RGB) { 16562306a36Sopenharmony_ci val |= RK3288_LVDS_CH0_REG0_TTL_EN | 16662306a36Sopenharmony_ci RK3288_LVDS_CH0_REG0_LANECK_EN; 16762306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val); 16862306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG2, 16962306a36Sopenharmony_ci RK3288_LVDS_PLL_FBDIV_REG2(0x46)); 17062306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, 17162306a36Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE | 17262306a36Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE | 17362306a36Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE | 17462306a36Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE | 17562306a36Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE | 17662306a36Sopenharmony_ci RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE); 17762306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, 17862306a36Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA | 17962306a36Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA | 18062306a36Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA | 18162306a36Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA | 18262306a36Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA | 18362306a36Sopenharmony_ci RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA); 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci val |= RK3288_LVDS_CH0_REG0_LVDS_EN | 18662306a36Sopenharmony_ci RK3288_LVDS_CH0_REG0_LANECK_EN; 18762306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val); 18862306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG1, 18962306a36Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANECK_BIAS | 19062306a36Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE4_BIAS | 19162306a36Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE3_BIAS | 19262306a36Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE2_BIAS | 19362306a36Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE1_BIAS | 19462306a36Sopenharmony_ci RK3288_LVDS_CH0_REG1_LANE0_BIAS); 19562306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG2, 19662306a36Sopenharmony_ci RK3288_LVDS_CH0_REG2_RESERVE_ON | 19762306a36Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE | 19862306a36Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE | 19962306a36Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE | 20062306a36Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE | 20162306a36Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE | 20262306a36Sopenharmony_ci RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE | 20362306a36Sopenharmony_ci RK3288_LVDS_PLL_FBDIV_REG2(0x46)); 20462306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00); 20562306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG3, 20862306a36Sopenharmony_ci RK3288_LVDS_PLL_FBDIV_REG3(0x46)); 20962306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REGD, 21062306a36Sopenharmony_ci RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); 21162306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CH0_REG20, 21262306a36Sopenharmony_ci RK3288_LVDS_CH0_REG20_LSB); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CFG_REGC, 21562306a36Sopenharmony_ci RK3288_LVDS_CFG_REGC_PLL_ENABLE); 21662306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CFG_REG21, 21762306a36Sopenharmony_ci RK3288_LVDS_CFG_REG21_TX_ENABLE); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void rk3288_lvds_poweroff(struct rockchip_lvds *lvds) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci int ret; 22562306a36Sopenharmony_ci u32 val; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CFG_REG21, 22862306a36Sopenharmony_ci RK3288_LVDS_CFG_REG21_TX_ENABLE); 22962306a36Sopenharmony_ci rk3288_writel(lvds, RK3288_LVDS_CFG_REGC, 23062306a36Sopenharmony_ci RK3288_LVDS_CFG_REGC_PLL_ENABLE); 23162306a36Sopenharmony_ci val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN; 23262306a36Sopenharmony_ci val |= val << 16; 23362306a36Sopenharmony_ci ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val); 23462306a36Sopenharmony_ci if (ret != 0) 23562306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci pm_runtime_put(lvds->dev); 23862306a36Sopenharmony_ci clk_disable(lvds->pclk); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int rk3288_lvds_grf_config(struct drm_encoder *encoder, 24262306a36Sopenharmony_ci struct drm_display_mode *mode) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 24562306a36Sopenharmony_ci u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0; 24662306a36Sopenharmony_ci u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0; 24762306a36Sopenharmony_ci u32 val; 24862306a36Sopenharmony_ci int ret; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* iomux to LCD data/sync mode */ 25162306a36Sopenharmony_ci if (lvds->output == DISPLAY_OUTPUT_RGB) 25262306a36Sopenharmony_ci if (lvds->pins && !IS_ERR(lvds->pins->default_state)) 25362306a36Sopenharmony_ci pinctrl_select_state(lvds->pins->p, 25462306a36Sopenharmony_ci lvds->pins->default_state); 25562306a36Sopenharmony_ci val = lvds->format | LVDS_CH0_EN; 25662306a36Sopenharmony_ci if (lvds->output == DISPLAY_OUTPUT_RGB) 25762306a36Sopenharmony_ci val |= LVDS_TTL_EN | LVDS_CH1_EN; 25862306a36Sopenharmony_ci else if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS) 25962306a36Sopenharmony_ci val |= LVDS_DUAL | LVDS_CH1_EN; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if ((mode->htotal - mode->hsync_start) & 0x01) 26262306a36Sopenharmony_ci val |= LVDS_START_PHASE_RST_1; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci val |= (pin_dclk << 8) | (pin_hsync << 9); 26562306a36Sopenharmony_ci val |= (0xffff << 16); 26662306a36Sopenharmony_ci ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val); 26762306a36Sopenharmony_ci if (ret) 26862306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int rk3288_lvds_set_vop_source(struct rockchip_lvds *lvds, 27462306a36Sopenharmony_ci struct drm_encoder *encoder) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci u32 val; 27762306a36Sopenharmony_ci int ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder); 28062306a36Sopenharmony_ci if (ret < 0) 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16; 28462306a36Sopenharmony_ci if (ret) 28562306a36Sopenharmony_ci val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON6, val); 28862306a36Sopenharmony_ci if (ret < 0) 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void rk3288_lvds_encoder_enable(struct drm_encoder *encoder) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 29762306a36Sopenharmony_ci struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; 29862306a36Sopenharmony_ci int ret; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci drm_panel_prepare(lvds->panel); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci ret = rk3288_lvds_poweron(lvds); 30362306a36Sopenharmony_ci if (ret < 0) { 30462306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret); 30562306a36Sopenharmony_ci drm_panel_unprepare(lvds->panel); 30662306a36Sopenharmony_ci return; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ret = rk3288_lvds_grf_config(encoder, mode); 31062306a36Sopenharmony_ci if (ret) { 31162306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret); 31262306a36Sopenharmony_ci drm_panel_unprepare(lvds->panel); 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = rk3288_lvds_set_vop_source(lvds, encoder); 31762306a36Sopenharmony_ci if (ret) { 31862306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n", ret); 31962306a36Sopenharmony_ci drm_panel_unprepare(lvds->panel); 32062306a36Sopenharmony_ci return; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci drm_panel_enable(lvds->panel); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void rk3288_lvds_encoder_disable(struct drm_encoder *encoder) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci drm_panel_disable(lvds->panel); 33162306a36Sopenharmony_ci rk3288_lvds_poweroff(lvds); 33262306a36Sopenharmony_ci drm_panel_unprepare(lvds->panel); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int px30_lvds_poweron(struct rockchip_lvds *lvds) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int ret; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(lvds->dev); 34062306a36Sopenharmony_ci if (ret < 0) { 34162306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret); 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Enable LVDS mode */ 34662306a36Sopenharmony_ci ret = regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 34762306a36Sopenharmony_ci PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1), 34862306a36Sopenharmony_ci PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1)); 34962306a36Sopenharmony_ci if (ret) 35062306a36Sopenharmony_ci pm_runtime_put(lvds->dev); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return ret; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void px30_lvds_poweroff(struct rockchip_lvds *lvds) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 35862306a36Sopenharmony_ci PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1), 35962306a36Sopenharmony_ci PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0)); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci pm_runtime_put(lvds->dev); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int px30_lvds_grf_config(struct drm_encoder *encoder, 36562306a36Sopenharmony_ci struct drm_display_mode *mode) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (lvds->output != DISPLAY_OUTPUT_LVDS) { 37062306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "Unsupported display output %d\n", 37162306a36Sopenharmony_ci lvds->output); 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Set format */ 37662306a36Sopenharmony_ci return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 37762306a36Sopenharmony_ci PX30_LVDS_FORMAT(lvds->format), 37862306a36Sopenharmony_ci PX30_LVDS_FORMAT(lvds->format)); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int px30_lvds_set_vop_source(struct rockchip_lvds *lvds, 38262306a36Sopenharmony_ci struct drm_encoder *encoder) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci int vop; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci vop = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder); 38762306a36Sopenharmony_ci if (vop < 0) 38862306a36Sopenharmony_ci return vop; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 39162306a36Sopenharmony_ci PX30_LVDS_VOP_SEL(1), 39262306a36Sopenharmony_ci PX30_LVDS_VOP_SEL(vop)); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void px30_lvds_encoder_enable(struct drm_encoder *encoder) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 39862306a36Sopenharmony_ci struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; 39962306a36Sopenharmony_ci int ret; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci drm_panel_prepare(lvds->panel); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ret = px30_lvds_poweron(lvds); 40462306a36Sopenharmony_ci if (ret) { 40562306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret); 40662306a36Sopenharmony_ci drm_panel_unprepare(lvds->panel); 40762306a36Sopenharmony_ci return; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = px30_lvds_grf_config(encoder, mode); 41162306a36Sopenharmony_ci if (ret) { 41262306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret); 41362306a36Sopenharmony_ci drm_panel_unprepare(lvds->panel); 41462306a36Sopenharmony_ci return; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ret = px30_lvds_set_vop_source(lvds, encoder); 41862306a36Sopenharmony_ci if (ret) { 41962306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n", ret); 42062306a36Sopenharmony_ci drm_panel_unprepare(lvds->panel); 42162306a36Sopenharmony_ci return; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci drm_panel_enable(lvds->panel); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic void px30_lvds_encoder_disable(struct drm_encoder *encoder) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct rockchip_lvds *lvds = encoder_to_lvds(encoder); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci drm_panel_disable(lvds->panel); 43262306a36Sopenharmony_ci px30_lvds_poweroff(lvds); 43362306a36Sopenharmony_ci drm_panel_unprepare(lvds->panel); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic const 43762306a36Sopenharmony_cistruct drm_encoder_helper_funcs rk3288_lvds_encoder_helper_funcs = { 43862306a36Sopenharmony_ci .enable = rk3288_lvds_encoder_enable, 43962306a36Sopenharmony_ci .disable = rk3288_lvds_encoder_disable, 44062306a36Sopenharmony_ci .atomic_check = rockchip_lvds_encoder_atomic_check, 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic const 44462306a36Sopenharmony_cistruct drm_encoder_helper_funcs px30_lvds_encoder_helper_funcs = { 44562306a36Sopenharmony_ci .enable = px30_lvds_encoder_enable, 44662306a36Sopenharmony_ci .disable = px30_lvds_encoder_disable, 44762306a36Sopenharmony_ci .atomic_check = rockchip_lvds_encoder_atomic_check, 44862306a36Sopenharmony_ci}; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int rk3288_lvds_probe(struct platform_device *pdev, 45162306a36Sopenharmony_ci struct rockchip_lvds *lvds) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci int ret; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci lvds->regs = devm_platform_ioremap_resource(pdev, 0); 45662306a36Sopenharmony_ci if (IS_ERR(lvds->regs)) 45762306a36Sopenharmony_ci return PTR_ERR(lvds->regs); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci lvds->pclk = devm_clk_get(lvds->dev, "pclk_lvds"); 46062306a36Sopenharmony_ci if (IS_ERR(lvds->pclk)) { 46162306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "could not get pclk_lvds\n"); 46262306a36Sopenharmony_ci return PTR_ERR(lvds->pclk); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins), 46662306a36Sopenharmony_ci GFP_KERNEL); 46762306a36Sopenharmony_ci if (!lvds->pins) 46862306a36Sopenharmony_ci return -ENOMEM; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci lvds->pins->p = devm_pinctrl_get(lvds->dev); 47162306a36Sopenharmony_ci if (IS_ERR(lvds->pins->p)) { 47262306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "no pinctrl handle\n"); 47362306a36Sopenharmony_ci devm_kfree(lvds->dev, lvds->pins); 47462306a36Sopenharmony_ci lvds->pins = NULL; 47562306a36Sopenharmony_ci } else { 47662306a36Sopenharmony_ci lvds->pins->default_state = 47762306a36Sopenharmony_ci pinctrl_lookup_state(lvds->pins->p, "lcdc"); 47862306a36Sopenharmony_ci if (IS_ERR(lvds->pins->default_state)) { 47962306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "no default pinctrl state\n"); 48062306a36Sopenharmony_ci devm_kfree(lvds->dev, lvds->pins); 48162306a36Sopenharmony_ci lvds->pins = NULL; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ret = clk_prepare(lvds->pclk); 48662306a36Sopenharmony_ci if (ret < 0) { 48762306a36Sopenharmony_ci DRM_DEV_ERROR(lvds->dev, "failed to prepare pclk_lvds\n"); 48862306a36Sopenharmony_ci return ret; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int px30_lvds_probe(struct platform_device *pdev, 49562306a36Sopenharmony_ci struct rockchip_lvds *lvds) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci int ret; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* MSB */ 50062306a36Sopenharmony_ci ret = regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1, 50162306a36Sopenharmony_ci PX30_LVDS_MSBSEL(1), 50262306a36Sopenharmony_ci PX30_LVDS_MSBSEL(1)); 50362306a36Sopenharmony_ci if (ret) 50462306a36Sopenharmony_ci return ret; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* PHY */ 50762306a36Sopenharmony_ci lvds->dphy = devm_phy_get(&pdev->dev, "dphy"); 50862306a36Sopenharmony_ci if (IS_ERR(lvds->dphy)) 50962306a36Sopenharmony_ci return PTR_ERR(lvds->dphy); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ret = phy_init(lvds->dphy); 51262306a36Sopenharmony_ci if (ret) 51362306a36Sopenharmony_ci return ret; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci ret = phy_set_mode(lvds->dphy, PHY_MODE_LVDS); 51662306a36Sopenharmony_ci if (ret) 51762306a36Sopenharmony_ci return ret; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return phy_power_on(lvds->dphy); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic const struct rockchip_lvds_soc_data rk3288_lvds_data = { 52362306a36Sopenharmony_ci .probe = rk3288_lvds_probe, 52462306a36Sopenharmony_ci .helper_funcs = &rk3288_lvds_encoder_helper_funcs, 52562306a36Sopenharmony_ci}; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic const struct rockchip_lvds_soc_data px30_lvds_data = { 52862306a36Sopenharmony_ci .probe = px30_lvds_probe, 52962306a36Sopenharmony_ci .helper_funcs = &px30_lvds_encoder_helper_funcs, 53062306a36Sopenharmony_ci}; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic const struct of_device_id rockchip_lvds_dt_ids[] = { 53362306a36Sopenharmony_ci { 53462306a36Sopenharmony_ci .compatible = "rockchip,rk3288-lvds", 53562306a36Sopenharmony_ci .data = &rk3288_lvds_data 53662306a36Sopenharmony_ci }, 53762306a36Sopenharmony_ci { 53862306a36Sopenharmony_ci .compatible = "rockchip,px30-lvds", 53962306a36Sopenharmony_ci .data = &px30_lvds_data 54062306a36Sopenharmony_ci }, 54162306a36Sopenharmony_ci {} 54262306a36Sopenharmony_ci}; 54362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int rockchip_lvds_bind(struct device *dev, struct device *master, 54662306a36Sopenharmony_ci void *data) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct rockchip_lvds *lvds = dev_get_drvdata(dev); 54962306a36Sopenharmony_ci struct drm_device *drm_dev = data; 55062306a36Sopenharmony_ci struct drm_encoder *encoder; 55162306a36Sopenharmony_ci struct drm_connector *connector; 55262306a36Sopenharmony_ci struct device_node *remote = NULL; 55362306a36Sopenharmony_ci struct device_node *port, *endpoint; 55462306a36Sopenharmony_ci int ret = 0, child_count = 0; 55562306a36Sopenharmony_ci const char *name; 55662306a36Sopenharmony_ci u32 endpoint_id = 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci lvds->drm_dev = drm_dev; 55962306a36Sopenharmony_ci port = of_graph_get_port_by_id(dev->of_node, 1); 56062306a36Sopenharmony_ci if (!port) { 56162306a36Sopenharmony_ci DRM_DEV_ERROR(dev, 56262306a36Sopenharmony_ci "can't found port point, please init lvds panel port!\n"); 56362306a36Sopenharmony_ci return -EINVAL; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci for_each_child_of_node(port, endpoint) { 56662306a36Sopenharmony_ci child_count++; 56762306a36Sopenharmony_ci of_property_read_u32(endpoint, "reg", &endpoint_id); 56862306a36Sopenharmony_ci ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id, 56962306a36Sopenharmony_ci &lvds->panel, &lvds->bridge); 57062306a36Sopenharmony_ci if (!ret) { 57162306a36Sopenharmony_ci of_node_put(endpoint); 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci if (!child_count) { 57662306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "lvds port does not have any children\n"); 57762306a36Sopenharmony_ci ret = -EINVAL; 57862306a36Sopenharmony_ci goto err_put_port; 57962306a36Sopenharmony_ci } else if (ret) { 58062306a36Sopenharmony_ci dev_err_probe(dev, ret, "failed to find panel and bridge node\n"); 58162306a36Sopenharmony_ci goto err_put_port; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci if (lvds->panel) 58462306a36Sopenharmony_ci remote = lvds->panel->dev->of_node; 58562306a36Sopenharmony_ci else 58662306a36Sopenharmony_ci remote = lvds->bridge->of_node; 58762306a36Sopenharmony_ci if (of_property_read_string(dev->of_node, "rockchip,output", &name)) 58862306a36Sopenharmony_ci /* default set it as output rgb */ 58962306a36Sopenharmony_ci lvds->output = DISPLAY_OUTPUT_RGB; 59062306a36Sopenharmony_ci else 59162306a36Sopenharmony_ci lvds->output = rockchip_lvds_name_to_output(name); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (lvds->output < 0) { 59462306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name); 59562306a36Sopenharmony_ci ret = lvds->output; 59662306a36Sopenharmony_ci goto err_put_remote; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (of_property_read_string(remote, "data-mapping", &name)) 60062306a36Sopenharmony_ci /* default set it as format vesa 18 */ 60162306a36Sopenharmony_ci lvds->format = LVDS_VESA_18; 60262306a36Sopenharmony_ci else 60362306a36Sopenharmony_ci lvds->format = rockchip_lvds_name_to_format(name); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (lvds->format < 0) { 60662306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name); 60762306a36Sopenharmony_ci ret = lvds->format; 60862306a36Sopenharmony_ci goto err_put_remote; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci encoder = &lvds->encoder.encoder; 61262306a36Sopenharmony_ci encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, 61362306a36Sopenharmony_ci dev->of_node); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_LVDS); 61662306a36Sopenharmony_ci if (ret < 0) { 61762306a36Sopenharmony_ci DRM_DEV_ERROR(drm_dev->dev, 61862306a36Sopenharmony_ci "failed to initialize encoder: %d\n", ret); 61962306a36Sopenharmony_ci goto err_put_remote; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci drm_encoder_helper_add(encoder, lvds->soc_data->helper_funcs); 62362306a36Sopenharmony_ci connector = &lvds->connector; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (lvds->panel) { 62662306a36Sopenharmony_ci connector->dpms = DRM_MODE_DPMS_OFF; 62762306a36Sopenharmony_ci ret = drm_connector_init(drm_dev, connector, 62862306a36Sopenharmony_ci &rockchip_lvds_connector_funcs, 62962306a36Sopenharmony_ci DRM_MODE_CONNECTOR_LVDS); 63062306a36Sopenharmony_ci if (ret < 0) { 63162306a36Sopenharmony_ci DRM_DEV_ERROR(drm_dev->dev, 63262306a36Sopenharmony_ci "failed to initialize connector: %d\n", ret); 63362306a36Sopenharmony_ci goto err_free_encoder; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci drm_connector_helper_add(connector, 63762306a36Sopenharmony_ci &rockchip_lvds_connector_helper_funcs); 63862306a36Sopenharmony_ci } else { 63962306a36Sopenharmony_ci ret = drm_bridge_attach(encoder, lvds->bridge, NULL, 64062306a36Sopenharmony_ci DRM_BRIDGE_ATTACH_NO_CONNECTOR); 64162306a36Sopenharmony_ci if (ret) 64262306a36Sopenharmony_ci goto err_free_encoder; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci connector = drm_bridge_connector_init(lvds->drm_dev, encoder); 64562306a36Sopenharmony_ci if (IS_ERR(connector)) { 64662306a36Sopenharmony_ci DRM_DEV_ERROR(drm_dev->dev, 64762306a36Sopenharmony_ci "failed to initialize bridge connector: %pe\n", 64862306a36Sopenharmony_ci connector); 64962306a36Sopenharmony_ci ret = PTR_ERR(connector); 65062306a36Sopenharmony_ci goto err_free_encoder; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ret = drm_connector_attach_encoder(connector, encoder); 65562306a36Sopenharmony_ci if (ret < 0) { 65662306a36Sopenharmony_ci DRM_DEV_ERROR(drm_dev->dev, 65762306a36Sopenharmony_ci "failed to attach encoder: %d\n", ret); 65862306a36Sopenharmony_ci goto err_free_connector; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci pm_runtime_enable(dev); 66262306a36Sopenharmony_ci of_node_put(remote); 66362306a36Sopenharmony_ci of_node_put(port); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cierr_free_connector: 66862306a36Sopenharmony_ci drm_connector_cleanup(connector); 66962306a36Sopenharmony_cierr_free_encoder: 67062306a36Sopenharmony_ci drm_encoder_cleanup(encoder); 67162306a36Sopenharmony_cierr_put_remote: 67262306a36Sopenharmony_ci of_node_put(remote); 67362306a36Sopenharmony_cierr_put_port: 67462306a36Sopenharmony_ci of_node_put(port); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return ret; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic void rockchip_lvds_unbind(struct device *dev, struct device *master, 68062306a36Sopenharmony_ci void *data) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct rockchip_lvds *lvds = dev_get_drvdata(dev); 68362306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *encoder_funcs; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci encoder_funcs = lvds->soc_data->helper_funcs; 68662306a36Sopenharmony_ci encoder_funcs->disable(&lvds->encoder.encoder); 68762306a36Sopenharmony_ci pm_runtime_disable(dev); 68862306a36Sopenharmony_ci drm_connector_cleanup(&lvds->connector); 68962306a36Sopenharmony_ci drm_encoder_cleanup(&lvds->encoder.encoder); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic const struct component_ops rockchip_lvds_component_ops = { 69362306a36Sopenharmony_ci .bind = rockchip_lvds_bind, 69462306a36Sopenharmony_ci .unbind = rockchip_lvds_unbind, 69562306a36Sopenharmony_ci}; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int rockchip_lvds_probe(struct platform_device *pdev) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 70062306a36Sopenharmony_ci struct rockchip_lvds *lvds; 70162306a36Sopenharmony_ci const struct of_device_id *match; 70262306a36Sopenharmony_ci int ret; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (!dev->of_node) 70562306a36Sopenharmony_ci return -ENODEV; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); 70862306a36Sopenharmony_ci if (!lvds) 70962306a36Sopenharmony_ci return -ENOMEM; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci lvds->dev = dev; 71262306a36Sopenharmony_ci match = of_match_node(rockchip_lvds_dt_ids, dev->of_node); 71362306a36Sopenharmony_ci if (!match) 71462306a36Sopenharmony_ci return -ENODEV; 71562306a36Sopenharmony_ci lvds->soc_data = match->data; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node, 71862306a36Sopenharmony_ci "rockchip,grf"); 71962306a36Sopenharmony_ci if (IS_ERR(lvds->grf)) { 72062306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "missing rockchip,grf property\n"); 72162306a36Sopenharmony_ci return PTR_ERR(lvds->grf); 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ret = lvds->soc_data->probe(pdev, lvds); 72562306a36Sopenharmony_ci if (ret) { 72662306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "Platform initialization failed\n"); 72762306a36Sopenharmony_ci return ret; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci dev_set_drvdata(dev, lvds); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci ret = component_add(&pdev->dev, &rockchip_lvds_component_ops); 73362306a36Sopenharmony_ci if (ret < 0) { 73462306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to add component\n"); 73562306a36Sopenharmony_ci clk_unprepare(lvds->pclk); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return ret; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic void rockchip_lvds_remove(struct platform_device *pdev) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct rockchip_lvds *lvds = platform_get_drvdata(pdev); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci component_del(&pdev->dev, &rockchip_lvds_component_ops); 74662306a36Sopenharmony_ci clk_unprepare(lvds->pclk); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistruct platform_driver rockchip_lvds_driver = { 75062306a36Sopenharmony_ci .probe = rockchip_lvds_probe, 75162306a36Sopenharmony_ci .remove_new = rockchip_lvds_remove, 75262306a36Sopenharmony_ci .driver = { 75362306a36Sopenharmony_ci .name = "rockchip-lvds", 75462306a36Sopenharmony_ci .of_match_table = of_match_ptr(rockchip_lvds_dt_ids), 75562306a36Sopenharmony_ci }, 75662306a36Sopenharmony_ci}; 757