From: Huang Jiachai Date: Wed, 19 Apr 2017 12:29:29 +0000 (+0800) Subject: drm/rockchip: lvds: add support rk3368 lvds X-Git-Tag: release-20171130_firefly~4^2~728 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=07bac5e0b8728590a0921aa9d9e5bd4e5f8c8c3f;p=firefly-linux-kernel-4.4.55.git drm/rockchip: lvds: add support rk3368 lvds Change-Id: I288fd42d9591119fadcbede67ff74be52d594e02 Signed-off-by: Huang Jiachai --- diff --git a/Documentation/devicetree/bindings/video/rockchip-lvds.txt b/Documentation/devicetree/bindings/video/rockchip-lvds.txt index 80529f429cc5..51f4864b8c21 100644 --- a/Documentation/devicetree/bindings/video/rockchip-lvds.txt +++ b/Documentation/devicetree/bindings/video/rockchip-lvds.txt @@ -2,10 +2,16 @@ Rockchip RK3288 LVDS interface ================================ Required properties: -- compatible: "rockchip,rk3288-lvds"; +- compatible: matching the soc type, one of + - "rockchip,rk3288-lvds"; + - "rockchip,rk33xx-lvds"; - reg: physical base address of the controller and length of memory mapped region. +- reg-names: the name to indicate register. example: + - "mipi_lvds_phy": lvds phy register, this's included in the MIPI phy module + - "mipi_lvds_ctl": lvds control register, this's included in the MIPI + controller module - clocks: must include clock specifiers corresponding to entries in the clock-names property. - clock-names: must contain "pclk_lvds" @@ -23,6 +29,11 @@ Required properties: - rockchip,output: should be "rgb", "lvds" or "duallvds", This describes the output face. +Optional properties +- pinctrl-names: must contain a "default" entry. +- pinctrl-0: pin control group to be used for this controller. +- pinctrl-1: pin control group to be used for gpio. + Required nodes: The lvds has two video ports as described by @@ -34,6 +45,9 @@ Their connections are modeled using the OF graph bindings specified in - video port 1 for either a panel or subsequent encoder Example: + +For Rockchip RK3288: + lvds: lvds@ff96c000 { compatible = "rockchip,rk3288-lvds"; rockchip,grf = <&grf>; @@ -72,3 +86,24 @@ Example: }; }; }; + +For Rockchip RK3368: + + lvds: lvds@ff968000 { + compatible = "rockchip,rk33xx-lvds"; + reg = <0x0 0xff968000 0x0 0x4000>, <0x0 0xff9600a0 0x0 0x20>; + reg-names = "mipi_lvds_phy", "mipi_lvds_ctl"; + clocks = <&cru PCLK_DPHYTX0>, <&cru PCLK_MIPI_DSI0>; + clock-names = "pclk_lvds", "pclk_lvds_ctl"; + power-domains = <&power RK3368_PD_VIO>; + rockchip,grf = <&grf>; + pinctrl-names = "lcdc", "gpio"; + pinctrl-0 = <&lcdc_lcdc>; + pinctrl-1 = <&lcdc_gpio>; + + ports { + + ... + + }; + }; \ No newline at end of file diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index aa0c49a3b90d..cdf061b57262 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -43,21 +43,27 @@ #define encoder_to_lvds(c) \ container_of(c, struct rockchip_lvds, encoder) +#define LVDS_CHIP(lvds) ((lvds)->soc_data->chip_type) /* * @grf_offset: offset inside the grf regmap for setting the rockchip lvds */ struct rockchip_lvds_soc_data { + int chip_type; + int grf_soc_con5; int grf_soc_con6; int grf_soc_con7; + int grf_soc_con15; }; struct rockchip_lvds { void *base; struct device *dev; void __iomem *regs; + void __iomem *regs_ctrl; struct regmap *grf; struct clk *pclk; + struct clk *pclk_ctrl; const struct rockchip_lvds_soc_data *soc_data; int output; @@ -70,12 +76,38 @@ struct rockchip_lvds { struct mutex suspend_lock; int suspend; + struct dev_pin_info *pins; }; static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val) { writel_relaxed(val, lvds->regs + offset); - writel_relaxed(val, lvds->regs + offset + 0x100); + if ((lvds->output == DISPLAY_OUTPUT_DUAL_LVDS) && + (LVDS_CHIP(lvds) == RK3288_LVDS)) + writel_relaxed(val, lvds->regs + offset + 0x100); +} + +static inline void lvds_msk_reg(struct rockchip_lvds *lvds, u32 offset, + u32 msk, u32 val) +{ + u32 temp; + + temp = readl_relaxed(lvds->regs + offset) & (0xFF - (msk)); + writel_relaxed(temp | ((val) & (msk)), lvds->regs + offset); +} + +static inline void lvds_dsi_writel(struct rockchip_lvds *lvds, + u32 offset, u32 val) +{ + writel_relaxed(val, lvds->regs_ctrl + offset); +} + +static inline u32 lvds_phy_lockon(struct rockchip_lvds *lvds) +{ + u32 val = 0; + + val = readl_relaxed(lvds->regs_ctrl + 0x10); + return (val & 0x01); } static inline int lvds_name_to_format(const char *s) @@ -106,22 +138,8 @@ static inline int lvds_name_to_output(const char *s) return -EINVAL; } -static int rockchip_lvds_poweron(struct rockchip_lvds *lvds) +static int rk3288_lvds_poweron(struct rockchip_lvds *lvds) { - int ret; - - ret = clk_enable(lvds->pclk); - if (ret < 0) { - dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret); - return ret; - } - - ret = pm_runtime_get_sync(lvds->dev); - if (ret < 0) { - dev_err(lvds->dev, "failed to get pm runtime: %d\n", ret); - return ret; - } - if (lvds->output == DISPLAY_OUTPUT_RGB) { lvds_writel(lvds, RK3288_LVDS_CH0_REG0, RK3288_LVDS_CH0_REG0_TTL_EN | @@ -197,22 +215,171 @@ static int rockchip_lvds_poweron(struct rockchip_lvds *lvds) return 0; } -static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds) +static int rk3368_lvds_poweron(struct rockchip_lvds *lvds) +{ + u32 delay_times = 20; + + if (lvds->output == DISPLAY_OUTPUT_LVDS) { + /* set VOCM 900 mv and V-DIFF 350 mv */ + lvds_msk_reg(lvds, MIPIPHY_REGE4, m_VOCM | m_DIFF_V, + v_VOCM(0) | v_DIFF_V(2)); + /* power up lvds pll and ldo */ + lvds_msk_reg(lvds, MIPIPHY_REG1, + m_SYNC_RST | m_LDO_PWR_DOWN | m_PLL_PWR_DOWN, + v_SYNC_RST(0) | v_LDO_PWR_DOWN(0) | + v_PLL_PWR_DOWN(0)); + /* enable lvds lane and power on pll */ + lvds_writel(lvds, MIPIPHY_REGEB, + v_LANE0_EN(1) | v_LANE1_EN(1) | v_LANE2_EN(1) | + v_LANE3_EN(1) | v_LANECLK_EN(1) | v_PLL_PWR_OFF(0)); + + /* enable lvds */ + lvds_msk_reg(lvds, MIPIPHY_REGE3, + m_MIPI_EN | m_LVDS_EN | m_TTL_EN, + v_MIPI_EN(0) | v_LVDS_EN(1) | v_TTL_EN(0)); + } else { + lvds_msk_reg(lvds, MIPIPHY_REGE3, + m_MIPI_EN | m_LVDS_EN | m_TTL_EN, + v_MIPI_EN(0) | v_LVDS_EN(0) | v_TTL_EN(1)); + } + /* delay for waitting pll lock on */ + while (delay_times--) { + if (lvds_phy_lockon(lvds)) + break; + udelay(100); + } + + if (delay_times <= 0) + dev_err(lvds->dev, "wait phy lockon failed, please check hardware\n"); + + return 0; +} + +static void rk3368_output_ttl(struct rockchip_lvds *lvds) +{ + u32 val = 0; + + /* enable lane */ + lvds_writel(lvds, MIPIPHY_REG0, 0x7f); + val = v_LANE0_EN(1) | v_LANE1_EN(1) | v_LANE2_EN(1) | v_LANE3_EN(1) | + v_LANECLK_EN(1) | v_PLL_PWR_OFF(1); + lvds_writel(lvds, MIPIPHY_REGEB, val); + + /* set ttl mode and reset phy config */ + val = v_LVDS_MODE_EN(0) | v_TTL_MODE_EN(1) | v_MIPI_MODE_EN(0) | + v_MSB_SEL(1) | v_DIG_INTER_RST(1); + lvds_writel(lvds, MIPIPHY_REGE0, val); + + rk3368_lvds_poweron(lvds); +} + +static void rk3368_output_lvds(struct rockchip_lvds *lvds) +{ + /* digital internal disable */ + lvds_msk_reg(lvds, MIPIPHY_REGE1, m_DIG_INTER_EN, v_DIG_INTER_EN(0)); + + /* set pll prediv and fbdiv */ + lvds_writel(lvds, MIPIPHY_REG3, v_PREDIV(2) | v_FBDIV_MSB(0)); + lvds_writel(lvds, MIPIPHY_REG4, v_FBDIV_LSB(28)); + + lvds_writel(lvds, MIPIPHY_REGE8, 0xfc); + + /* set lvds mode and reset phy config */ + lvds_msk_reg(lvds, MIPIPHY_REGE0, + m_MSB_SEL | m_DIG_INTER_RST, + v_MSB_SEL(1) | v_DIG_INTER_RST(1)); + + rk3368_lvds_poweron(lvds); + lvds_msk_reg(lvds, MIPIPHY_REGE1, m_DIG_INTER_EN, v_DIG_INTER_EN(1)); +} + +static int rk3368_lvds_output(struct rockchip_lvds *lvds) +{ + if (lvds->output == DISPLAY_OUTPUT_RGB) + rk3368_output_ttl(lvds); + else + rk3368_output_lvds(lvds); + return 0; +} + +static int rockchip_lvds_poweron(struct rockchip_lvds *lvds) { int ret; - ret = regmap_write(lvds->grf, - lvds->soc_data->grf_soc_con7, 0xffff8000); - if (ret != 0) - dev_err(lvds->dev, "Could not write to GRF: %d\n", ret); + if (lvds->pclk) { + ret = clk_enable(lvds->pclk); + if (ret < 0) { + dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret); + return ret; + } + } + if (lvds->pclk_ctrl) { + ret = clk_enable(lvds->pclk_ctrl); + if (ret < 0) { + dev_err(lvds->dev, "failed to enable lvds pclk_ctrl %d\n", ret); + return ret; + } + } - writel(RK3288_LVDS_CFG_REG21_TX_DISABLE, - lvds->regs + RK3288_LVDS_CFG_REG21); - writel(RK3288_LVDS_CFG_REGC_PLL_DISABLE, - lvds->regs + RK3288_LVDS_CFG_REGC); + ret = pm_runtime_get_sync(lvds->dev); + if (ret < 0) { + dev_err(lvds->dev, "failed to get pm runtime: %d\n", ret); + return ret; + } + if (LVDS_CHIP(lvds) == RK3288_LVDS) + rk3288_lvds_poweron(lvds); + else if (LVDS_CHIP(lvds) == RK3368_LVDS) + rk3368_lvds_output(lvds); + + return 0; +} + +static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds) +{ + int ret; + u32 val; + + if (LVDS_CHIP(lvds) == RK3288_LVDS) { + ret = regmap_write(lvds->grf, + lvds->soc_data->grf_soc_con7, 0xffff8000); + if (ret != 0) + dev_err(lvds->dev, "Could not write to GRF: %d\n", ret); + + writel(RK3288_LVDS_CFG_REG21_TX_DISABLE, + lvds->regs + RK3288_LVDS_CFG_REG21); + writel(RK3288_LVDS_CFG_REGC_PLL_DISABLE, + lvds->regs + RK3288_LVDS_CFG_REGC); + + pm_runtime_put(lvds->dev); + if (lvds->pclk) + clk_disable(lvds->pclk); + } else if (LVDS_CHIP(lvds) == RK3368_LVDS) { + val = v_RK3368_LVDSMODE_EN(0) | v_RK3368_MIPIPHY_TTL_EN(0); + ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val); + if (ret != 0) { + dev_err(lvds->dev, "Could not write to GRF: %d\n", ret); + return; + } - pm_runtime_put(lvds->dev); - clk_disable(lvds->pclk); + /* disable lvds lane and power off pll */ + lvds_writel(lvds, MIPIPHY_REGEB, + v_LANE0_EN(0) | v_LANE1_EN(0) | v_LANE2_EN(0) | + v_LANE3_EN(0) | v_LANECLK_EN(0) | v_PLL_PWR_OFF(1)); + + /* power down lvds pll and bandgap */ + lvds_msk_reg(lvds, MIPIPHY_REG1, + m_SYNC_RST | m_LDO_PWR_DOWN | m_PLL_PWR_DOWN, + v_SYNC_RST(1) | v_LDO_PWR_DOWN(1) | v_PLL_PWR_DOWN(1)); + + /* disable lvds */ + lvds_msk_reg(lvds, MIPIPHY_REGE3, m_LVDS_EN | m_TTL_EN, + v_LVDS_EN(0) | v_TTL_EN(0)); + pm_runtime_put(lvds->dev); + if (lvds->pclk) + clk_disable(lvds->pclk); + if (lvds->pclk_ctrl) + clk_disable(lvds->pclk_ctrl); + } } static enum drm_connector_status @@ -327,23 +494,70 @@ static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder, u32 val; int ret; - val = lvds->format; - if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS) - val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN; - else if (lvds->output == DISPLAY_OUTPUT_LVDS) - val |= LVDS_CH0_EN; - else if (lvds->output == DISPLAY_OUTPUT_RGB) - val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN; - - if (h_bp & 0x01) - val |= LVDS_START_PHASE_RST_1; - - val |= (pin_dclk << 8) | (pin_hsync << 9); - val |= (0xffff << 16); - ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val); - if (ret != 0) { - dev_err(lvds->dev, "Could not write to GRF: %d\n", ret); - return; + if (LVDS_CHIP(lvds) == RK3288_LVDS) { + val = lvds->format; + if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS) + val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN; + else if (lvds->output == DISPLAY_OUTPUT_LVDS) + val |= LVDS_CH0_EN; + else if (lvds->output == DISPLAY_OUTPUT_RGB) + val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN; + + if (h_bp & 0x01) + val |= LVDS_START_PHASE_RST_1; + + val |= (pin_dclk << 8) | (pin_hsync << 9); + val |= (0xffff << 16); + ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val); + if (ret != 0) { + dev_err(lvds->dev, "Could not write to GRF: %d\n", ret); + return; + } + } else if (LVDS_CHIP(lvds) == RK3368_LVDS) { + if (lvds->output == DISPLAY_OUTPUT_RGB) { + /* iomux to lcdc */ + if (lvds->pins && !IS_ERR(lvds->pins->default_state)) + pinctrl_select_state(lvds->pins->p, + lvds->pins->default_state); + + lvds_dsi_writel(lvds, 0x0, 0x4);/*set clock lane enable*/ + /* enable lvds mode */ + val = v_RK3368_LVDSMODE_EN(0) | + v_RK3368_MIPIPHY_TTL_EN(1) | + v_RK3368_MIPIPHY_LANE0_EN(1) | + v_RK3368_MIPIDPI_FORCEX_EN(1); + ret = regmap_write(lvds->grf, + lvds->soc_data->grf_soc_con7, val); + if (ret != 0) { + dev_err(lvds->dev, + "Could not write to GRF: %d\n", ret); + return; + } + val = v_RK3368_FORCE_JETAG(0); + ret = regmap_write(lvds->grf, + lvds->soc_data->grf_soc_con15, val); + if (ret != 0) { + dev_err(lvds->dev, + "Could not write to GRF: %d\n", ret); + return; + } + } else if (lvds->output == DISPLAY_OUTPUT_LVDS) { + /* enable lvds mode */ + val = v_RK3368_LVDSMODE_EN(1) | v_RK3368_MIPIPHY_TTL_EN(0); + /* config lvds_format */ + val |= v_RK3368_LVDS_OUTPUT_FORMAT(lvds->format); + /* LSB receive mode */ + val |= v_RK3368_LVDS_MSBSEL(LVDS_MSB_D7); + val |= v_RK3368_MIPIPHY_LANE0_EN(1) | + v_RK3368_MIPIDPI_FORCEX_EN(1); + ret = regmap_write(lvds->grf, + lvds->soc_data->grf_soc_con7, val); + if (ret != 0) { + dev_err(lvds->dev, + "Could not write to GRF: %d\n", ret); + return; + } + } } } @@ -357,16 +571,17 @@ static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds, if (ret < 0) return ret; - if (ret) - val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT | - (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16); - else - val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16; - - ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val); - if (ret < 0) - return ret; + if (LVDS_CHIP(lvds) == RK3288_LVDS) { + if (ret) + val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT | + (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16); + else + val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16; + ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val); + if (ret < 0) + return ret; + } return 0; } @@ -419,15 +634,28 @@ static struct drm_encoder_funcs rockchip_lvds_encoder_funcs = { }; static struct rockchip_lvds_soc_data rk3288_lvds_data = { + .chip_type = RK3288_LVDS, .grf_soc_con6 = 0x025c, .grf_soc_con7 = 0x0260, }; +static struct rockchip_lvds_soc_data rk33xx_lvds_data = { + .chip_type = RK3368_LVDS, + .grf_soc_con5 = 0x0414, + .grf_soc_con6 = 0x0418, + .grf_soc_con7 = 0x041c, + .grf_soc_con15 = 0x043c, +}; + static const struct of_device_id rockchip_lvds_dt_ids[] = { { .compatible = "rockchip,rk3288-lvds", .data = &rk3288_lvds_data }, + { + .compatible = "rockchip,rk33xx-lvds", + .data = &rk33xx_lvds_data + }, {} }; MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids); @@ -439,10 +667,71 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, struct drm_device *drm_dev = data; struct drm_encoder *encoder; struct drm_connector *connector; - int ret; - + struct device_node *panel_node, *port, *endpoint; + int ret, i; + const char *name; lvds->drm_dev = drm_dev; + port = of_graph_get_port_by_id(dev->of_node, 1); + if (port) { + endpoint = of_get_child_by_name(port, "endpoint"); + of_node_put(port); + if (!endpoint) { + dev_err(dev, "no output endpoint found\n"); + return -EINVAL; + } + panel_node = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + if (!panel_node) { + dev_err(dev, "no output node found\n"); + return -EINVAL; + } + lvds->panel = of_drm_find_panel(panel_node); + if (!lvds->panel) { + DRM_ERROR("failed to find panel\n"); + of_node_put(panel_node); + return -EPROBE_DEFER; + } + + if (of_property_read_string(panel_node, "rockchip,output", &name)) + /* default set it as output rgb */ + lvds->output = DISPLAY_OUTPUT_RGB; + else + lvds->output = lvds_name_to_output(name); + + if (lvds->output < 0) { + dev_err(dev, "invalid output type [%s]\n", name); + return lvds->output; + } + + if (of_property_read_string(panel_node, "rockchip,data-mapping", + &name)) + /* default set it as format jeida */ + lvds->format = LVDS_FORMAT_JEIDA; + else + lvds->format = lvds_name_to_format(name); + + if (lvds->format < 0) { + dev_err(dev, "invalid data-mapping format [%s]\n", name); + return lvds->format; + } + + if (of_property_read_u32(panel_node, "rockchip,data-width", &i)) { + lvds->format |= LVDS_24BIT; + } else { + if (i == 24) { + lvds->format |= LVDS_24BIT; + } else if (i == 18) { + lvds->format |= LVDS_18BIT; + } else { + dev_err(dev, + "rockchip-lvds unsupport data-width[%d]\n", i); + return -EINVAL; + } + } + of_node_put(panel_node); + } + encoder = &lvds->encoder; encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node); @@ -481,7 +770,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, DRM_ERROR("failed to attach connector and encoder\n"); goto err_free_connector; } - + lvds->connector.port = dev->of_node; pm_runtime_enable(dev); return 0; @@ -517,11 +806,9 @@ static int rockchip_lvds_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rockchip_lvds *lvds; - struct device_node *output_node = NULL; const struct of_device_id *match; struct resource *res; - const char *name; - int i, ret; + int ret; if (!dev->of_node) return -ENODEV; @@ -532,91 +819,95 @@ static int rockchip_lvds_probe(struct platform_device *pdev) lvds->dev = dev; lvds->suspend = true; + match = of_match_node(rockchip_lvds_dt_ids, dev->of_node); + lvds->soc_data = match->data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - lvds->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(lvds->regs)) - return PTR_ERR(lvds->regs); + if (LVDS_CHIP(lvds) == RK3288_LVDS) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lvds->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(lvds->regs)) + return PTR_ERR(lvds->regs); + } else if (LVDS_CHIP(lvds) == RK3368_LVDS) { + /* lvds regs on MIPIPHY_REG */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "mipi_lvds_phy"); + lvds->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(lvds->regs)) { + dev_err(&pdev->dev, "ioremap lvds phy reg failed\n"); + return PTR_ERR(lvds->regs); + } - lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node, - "rockchip,grf"); - if (IS_ERR(lvds->grf)) { - dev_err(dev, "missing rockchip,grf property\n"); - return PTR_ERR(lvds->grf); + /* pll lock on status reg that is MIPICTRL Register */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "mipi_lvds_ctl"); + lvds->regs_ctrl = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(lvds->regs_ctrl)) { + dev_err(&pdev->dev, "ioremap lvds ctl reg failed\n"); + return PTR_ERR(lvds->regs_ctrl); + } + /* mipi ctrl clk for read lvds phy lock state */ + lvds->pclk_ctrl = devm_clk_get(&pdev->dev, "pclk_lvds_ctl"); + if (IS_ERR(lvds->pclk_ctrl)) { + dev_err(dev, "could not get pclk_ctrl\n"); + lvds->pclk_ctrl = NULL; + } + lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins), + GFP_KERNEL); + if (!lvds->pins) + return -ENOMEM; + + lvds->pins->p = devm_pinctrl_get(lvds->dev); + if (IS_ERR(lvds->pins->p)) { + dev_info(lvds->dev, "no pinctrl handle\n"); + devm_kfree(lvds->dev, lvds->pins); + lvds->pins = NULL; + } else { + lvds->pins->default_state = + pinctrl_lookup_state(lvds->pins->p, "lcdc"); + if (IS_ERR(lvds->pins->default_state)) { + dev_info(lvds->dev, "no default pinctrl state\n"); + devm_kfree(lvds->dev, lvds->pins); + lvds->pins = NULL; + } + } } - lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds"); if (IS_ERR(lvds->pclk)) { dev_err(dev, "could not get pclk_lvds\n"); return PTR_ERR(lvds->pclk); } - - match = of_match_node(rockchip_lvds_dt_ids, dev->of_node); - lvds->soc_data = match->data; + lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(lvds->grf)) { + dev_err(dev, "missing rockchip,grf property\n"); + return PTR_ERR(lvds->grf); + } dev_set_drvdata(dev, lvds); mutex_init(&lvds->suspend_lock); - if (of_property_read_string(dev->of_node, "rockchip,output", &name)) - /* default set it as output rgb */ - lvds->output = DISPLAY_OUTPUT_RGB; - else - lvds->output = lvds_name_to_output(name); - - if (lvds->output < 0) { - dev_err(dev, "invalid output type [%s]\n", name); - return lvds->output; - } - - if (of_property_read_string(dev->of_node, "rockchip,data-mapping", - &name)) - /* default set it as format jeida */ - lvds->format = LVDS_FORMAT_JEIDA; - else - lvds->format = lvds_name_to_format(name); - - if (lvds->format < 0) { - dev_err(dev, "invalid data-mapping format [%s]\n", name); - return lvds->format; - } - - if (of_property_read_u32(dev->of_node, "rockchip,data-width", &i)) { - lvds->format |= LVDS_24BIT; - } else { - if (i == 24) { - lvds->format |= LVDS_24BIT; - } else if (i == 18) { - lvds->format |= LVDS_18BIT; - } else { - dev_err(&pdev->dev, - "rockchip-lvds unsupport data-width[%d]\n", i); - return -EINVAL; + if (lvds->pclk) { + ret = clk_prepare(lvds->pclk); + if (ret < 0) { + dev_err(dev, "failed to prepare pclk_lvds\n"); + return ret; } } - - output_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0); - if (!output_node) { - DRM_ERROR("failed to find rockchip,panel dt node\n"); - return -ENODEV; - } - - lvds->panel = of_drm_find_panel(output_node); - of_node_put(output_node); - if (!lvds->panel) { - DRM_ERROR("failed to find panel\n"); - return -EPROBE_DEFER; + if (lvds->pclk_ctrl) { + ret = clk_prepare(lvds->pclk_ctrl); + if (ret < 0) { + dev_err(dev, "failed to prepare pclk_ctrl lvds\n"); + return ret; + } } - - ret = clk_prepare(lvds->pclk); + ret = component_add(&pdev->dev, &rockchip_lvds_component_ops); if (ret < 0) { - dev_err(dev, "failed to prepare pclk_lvds\n"); - return ret; + if (lvds->pclk) + clk_unprepare(lvds->pclk); + if (lvds->pclk_ctrl) + clk_unprepare(lvds->pclk_ctrl); } - ret = component_add(&pdev->dev, &rockchip_lvds_component_ops); - if (ret < 0) - clk_unprepare(lvds->pclk); - return ret; } @@ -625,8 +916,10 @@ static int rockchip_lvds_remove(struct platform_device *pdev) struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev); component_del(&pdev->dev, &rockchip_lvds_component_ops); - clk_unprepare(lvds->pclk); - + if (lvds->pclk) + clk_unprepare(lvds->pclk); + if (lvds->pclk_ctrl) + clk_unprepare(lvds->pclk_ctrl); return 0; } diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h index 5b79651cb725..803566b37955 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.h +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.h @@ -105,4 +105,86 @@ #define LVDS_FORMAT_VESA (0 << 0) #define LVDS_FORMAT_JEIDA (1 << 0) +#define BITS(x, bit) ((x) << (bit)) +#define BITS_MASK(x, mask, bit) BITS((x) & (mask), bit) +#define BITS_EN(mask, bit) BITS(mask, bit + 16) + +#define MIPIPHY_REG0 0x0000 + +#define MIPIPHY_REG1 0x0004 +#define m_SYNC_RST BIT(0) +#define m_LDO_PWR_DOWN BIT(1) +#define m_PLL_PWR_DOWN BIT(2) +#define v_SYNC_RST(x) BITS_MASK(x, 1, 0) +#define v_LDO_PWR_DOWN(x) BITS_MASK(x, 1, 1) +#define v_PLL_PWR_DOWN(x) BITS_MASK(x, 1, 2) + +#define MIPIPHY_REG3 0x000c +#define m_PREDIV GENMASK(4, 0) +#define m_FBDIV_MSB BIT(5) +#define v_PREDIV(x) BITS_MASK(x, 0x1f, 0) +#define v_FBDIV_MSB(x) BITS_MASK(x, 1, 5) + +#define MIPIPHY_REG4 0x0010 +#define v_FBDIV_LSB(x) BITS_MASK(x, 0xff, 0) + +#define MIPIPHY_REGE0 0x0380 +#define m_MSB_SEL BIT(0) +#define m_DIG_INTER_RST BIT(2) +#define m_LVDS_MODE_EN BIT(5) +#define m_TTL_MODE_EN BIT(6) +#define m_MIPI_MODE_EN BIT(7) +#define v_MSB_SEL(x) BITS_MASK(x, 1, 0) +#define v_DIG_INTER_RST(x) BITS_MASK(x, 1, 2) +#define v_LVDS_MODE_EN(x) BITS_MASK(x, 1, 5) +#define v_TTL_MODE_EN(x) BITS_MASK(x, 1, 6) +#define v_MIPI_MODE_EN(x) BITS_MASK(x, 1, 7) + +#define MIPIPHY_REGE1 0x0384 +#define m_DIG_INTER_EN BIT(7) +#define v_DIG_INTER_EN(x) BITS_MASK(x, 1, 7) + +#define MIPIPHY_REGE3 0x038c +#define m_MIPI_EN BIT(0) +#define m_LVDS_EN BIT(1) +#define m_TTL_EN BIT(2) +#define v_MIPI_EN(x) BITS_MASK(x, 1, 0) +#define v_LVDS_EN(x) BITS_MASK(x, 1, 1) +#define v_TTL_EN(x) BITS_MASK(x, 1, 2) + +#define MIPIPHY_REGE4 0x0390 +#define m_VOCM GENMASK(5, 4) +#define m_DIFF_V GENMASK(7, 6) + +#define v_VOCM(x) BITS_MASK(x, 3, 4) +#define v_DIFF_V(x) BITS_MASK(x, 3, 6) + +#define MIPIPHY_REGE8 0x03a0 + +#define MIPIPHY_REGEB 0x03ac +#define v_PLL_PWR_OFF(x) BITS_MASK(x, 1, 2) +#define v_LANECLK_EN(x) BITS_MASK(x, 1, 3) +#define v_LANE3_EN(x) BITS_MASK(x, 1, 4) +#define v_LANE2_EN(x) BITS_MASK(x, 1, 5) +#define v_LANE1_EN(x) BITS_MASK(x, 1, 6) +#define v_LANE0_EN(x) BITS_MASK(x, 1, 7) + +#define v_RK3368_LVDS_OUTPUT_FORMAT(x) (BITS_MASK(x, 3, 13) | BITS_EN(3, 13)) +#define v_RK3368_LVDS_MSBSEL(x) (BITS_MASK(x, 1, 11) | BITS_EN(1, 11)) +#define v_RK3368_LVDSMODE_EN(x) (BITS_MASK(x, 1, 12) | BITS_EN(1, 12)) +#define v_RK3368_MIPIPHY_TTL_EN(x) (BITS_MASK(x, 1, 15) | BITS_EN(1, 15)) +#define v_RK3368_MIPIPHY_LANE0_EN(x) (BITS_MASK(x, 1, 5) | BITS_EN(1, 5)) +#define v_RK3368_MIPIDPI_FORCEX_EN(x) (BITS_MASK(x, 1, 6) | BITS_EN(1, 6)) +#define v_RK3368_FORCE_JETAG(x) (BITS_MASK(x, 1, 13) | BITS_EN(1, 13)) + +enum { + LVDS_MSB_D0 = 0, + LVDS_MSB_D7, +}; + +enum rockchip_lvds_sub_devtype { + RK3288_LVDS, + RK3368_LVDS, +}; + #endif /* _ROCKCHIP_LVDS_ */