Hi Philipp, Thanks for your fast respond :) On 01/07/2016 06:04 PM, Philipp Zabel wrote: > Am Donnerstag, den 07.01.2016, 17:02 +0800 schrieb Yakir Yang: >> RK3229 integrate an DesignedWare HDMI2.0 controller and an INNO HDMI2.0 phy, >> the max output resolution is 4K. >> >> Signed-off-by: Yakir Yang <ykk at rock-chips.com> > It sounds like the INNO HDMI2.0 phy is not necessarily specific to > RK3229 but might also appear in other SoCs? If so, I think this should > be implemented in a separate phy driver and be used by dw_hdmi-rockchip. Do you mean I should create a new phy driver that place in "driver/phy" directly ? I have think about this idea, and it would make things much clean. But INNO PHY driver need the target pixel clock in drm_display_mode, I didn't find a good way to pass this variable to separate phy driver. Do you have some idea ? Thanks, - Yakir > regards > Philipp > >> --- >> Changes in v2: >> - Split some dw-hdmi driver changes into separate patches [01/04] & [02/04] >> >> drivers/gpu/drm/bridge/dw-hdmi.c | 27 +- >> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 367 ++++++++++++++++++++++++++-- >> drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h | 137 +++++++++++ >> include/drm/bridge/dw_hdmi.h | 3 + >> 4 files changed, 507 insertions(+), 27 deletions(-) >> create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h >> >> diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c >> index 5ad72ec..5e03d83 100644 >> --- a/drivers/gpu/drm/bridge/dw-hdmi.c >> +++ b/drivers/gpu/drm/bridge/dw-hdmi.c >> @@ -735,10 +735,12 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, >> { >> unsigned res_idx; >> u8 val, msec; >> + int ret; >> const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; >> const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg; >> const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; >> const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; >> + int mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock; >> >> if (prep) >> return -EINVAL; >> @@ -758,27 +760,38 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, >> return -EINVAL; >> } >> >> + if (hdmi->plat_data->extphy_config) { >> + /* gen2 tx power off */ >> + dw_hdmi_phy_gen2_txpwron(hdmi, 0); >> + dw_hdmi_phy_gen2_pddq(hdmi, 1); >> + >> + ret = hdmi->plat_data->extphy_config(hdmi->plat_data, res_idx, >> + mpixelclock); >> + /* gen2 tx power on */ >> + dw_hdmi_phy_gen2_txpwron(hdmi, 1); >> + dw_hdmi_phy_gen2_pddq(hdmi, 0); >> + >> + return ret; >> + } >> + >> /* PLL/MPLL Cfg - always match on final entry */ >> for (; mpll_config->mpixelclock != ~0UL; mpll_config++) >> - if (hdmi->hdmi_data.video_mode.mpixelclock <= >> - mpll_config->mpixelclock) >> + if (mpixelclock <= mpll_config->mpixelclock) >> break; >> >> for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++) >> - if (hdmi->hdmi_data.video_mode.mpixelclock <= >> - curr_ctrl->mpixelclock) >> + if (mpixelclock <= curr_ctrl->mpixelclock) >> break; >> >> for (; phy_config->mpixelclock != ~0UL; phy_config++) >> - if (hdmi->hdmi_data.video_mode.mpixelclock <= >> - phy_config->mpixelclock) >> + if (mpixelclock <= phy_config->mpixelclock) >> break; >> >> if (mpll_config->mpixelclock == ~0UL || >> curr_ctrl->mpixelclock == ~0UL || >> phy_config->mpixelclock == ~0UL) { >> dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n", >> - hdmi->hdmi_data.video_mode.mpixelclock); >> + mpixelclock); >> return -EINVAL; >> } >> >> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c >> index 8164823..24fffaa 100644 >> --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c >> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c >> @@ -7,6 +7,7 @@ >> * (at your option) any later version. >> */ >> >> +#include <linux/clk.h> >> #include <linux/module.h> >> #include <linux/platform_device.h> >> #include <linux/mfd/syscon.h> >> @@ -21,18 +22,134 @@ >> #include "rockchip_drm_drv.h" >> #include "rockchip_drm_vop.h" >> >> -#define GRF_SOC_CON6 0x025c >> -#define HDMI_SEL_VOP_LIT (1 << 4) >> +#include "dw_hdmi-rockchip.h" >> >> struct rockchip_hdmi { >> struct device *dev; >> struct regmap *regmap; >> struct drm_encoder encoder; >> struct dw_hdmi_plat_data plat_data; >> + >> + void __iomem *extphy_regbase; >> + struct clk *extphy_pclk; >> }; >> >> #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) >> >> +static const struct extphy_config_tab rockchip_extphy_cfg[] = { >> + { .mpixelclock = 165000000, >> + .pre_emphasis = 0, .slopeboost = 0, .clk_level = 4, >> + .data0_level = 4, 4, 4, >> + }, >> + >> + { .mpixelclock = 225000000, >> + .pre_emphasis = 0, .slopeboost = 0, .clk_level = 6, >> + .data0_level = 6, 6, 6, >> + }, >> + >> + { .mpixelclock = 340000000, >> + .pre_emphasis = 1, .slopeboost = 0, .clk_level = 6, >> + .data0_level = 10, 10, 10, >> + }, >> + >> + { .mpixelclock = 594000000, >> + .pre_emphasis = 1, .slopeboost = 0, .clk_level = 7, >> + .data0_level = 10, 10, 10, >> + }, >> + >> + { .mpixelclock = ~0UL}, >> +}; >> + >> +static const struct extphy_pll_config_tab rockchip_extphy_pll_cfg[] = { >> + { >> + .mpixelclock = 27000000, .param = { >> + { .pll_nd = 1, .pll_nf = 45, >> + .tmsd_divider_a = 3, 1, 1, >> + .pclk_divider_a = 1, 3, 3, 4, >> + .vco_div_5 = 0, >> + .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8, >> + }, >> + { .pll_nd = 1, .pll_nf = 45, >> + .tmsd_divider_a = 0, 3, 3, >> + .pclk_divider_a = 1, 3, 3, 4, >> + .vco_div_5 = 0, >> + .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8, >> + }, >> + }, >> + }, { >> + .mpixelclock = 59400000, .param = { >> + { .pll_nd = 2, .pll_nf = 99, >> + .tmsd_divider_a = 3, 1, 1, >> + .pclk_divider_a = 1, 3, 2, 2, >> + .vco_div_5 = 0, >> + .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8, >> + }, >> + { .pll_nd = 2, .pll_nf = 99, >> + .tmsd_divider_a = 1, 1, 1, >> + .pclk_divider_a = 1, 3, 2, 2, >> + .vco_div_5 = 0, >> + .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8, >> + }, >> + }, >> + }, { >> + .mpixelclock = 74250000, .param = { >> + { .pll_nd = 2, .pll_nf = 99, >> + .tmsd_divider_a = 1, 1, 1, >> + .pclk_divider_a = 1, 2, 2, 2, >> + .vco_div_5 = 0, >> + .ppll_nd = 1, .ppll_nf = 40, .ppll_no = 8, >> + }, >> + { .pll_nd = 4, .pll_nf = 495, >> + .tmsd_divider_a = 1, 2, 2, >> + .pclk_divider_a = 1, 3, 3, 4, >> + .vco_div_5 = 0, >> + .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4, >> + }, >> + }, >> + }, { >> + .mpixelclock = 148500000, .param = { >> + { .pll_nd = 2, .pll_nf = 99, >> + .tmsd_divider_a = 1, 0, 0, >> + .pclk_divider_a = 1, 2, 1, 1, >> + .vco_div_5 = 0, >> + .ppll_nd = 2, .ppll_nf = 40, .ppll_no = 4, >> + }, >> + { .pll_nd = 4, .pll_nf = 495, >> + .tmsd_divider_a = 0, 2, 2, >> + .pclk_divider_a = 1, 3, 2, 2, >> + .vco_div_5 = 0, >> + .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2, >> + }, >> + }, >> + }, { >> + .mpixelclock = 297000000, .param = { >> + { .pll_nd = 2, .pll_nf = 99, >> + .tmsd_divider_a = 0, 0, 0, >> + .pclk_divider_a = 1, 0, 1, 1, >> + .vco_div_5 = 0, >> + .ppll_nd = 4, .ppll_nf = 40, .ppll_no = 2, >> + }, >> + { .pll_nd = 4, .pll_nf = 495, >> + .tmsd_divider_a = 1, 2, 0, >> + .pclk_divider_a = 1, 3, 1, 1, >> + .vco_div_5 = 0, >> + .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1, >> + }, >> + }, >> + }, { >> + .mpixelclock = 594000000, .param = { >> + { .pll_nd = 1, .pll_nf = 99, >> + .tmsd_divider_a = 0, 2, 0, >> + .pclk_divider_a = 1, 0, 1, 1, >> + .vco_div_5 = 0, >> + .ppll_nd = 8, .ppll_nf = 40, .ppll_no = 1, >> + }, >> + } >> + }, { >> + .mpixelclock = ~0UL, >> + } >> +}; >> + >> static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { >> { >> 27000000, { >> @@ -142,9 +259,164 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = { >> { ~0UL, 0x0000, 0x0000, 0x0000} >> }; >> >> +static inline void hdmi_extphy_write(struct rockchip_hdmi *hdmi, >> + unsigned short data, unsigned char addr) >> +{ >> + writel_relaxed(data, hdmi->extphy_regbase + (addr) * 0x04); >> +} >> + >> +static inline unsigned int hdmi_extphy_read(struct rockchip_hdmi *hdmi, >> + unsigned char addr) >> +{ >> + return readl_relaxed(hdmi->extphy_regbase + (addr) * 0x04); >> +} >> + >> +static int rockchip_extphy_config(const struct dw_hdmi_plat_data *plat_data, >> + int res, int pixelclock) >> +{ >> + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(plat_data); >> + const struct extphy_pll_config_tab *mpll = rockchip_extphy_pll_cfg; >> + const struct extphy_config_tab *ctrl = rockchip_extphy_cfg; >> + const struct extphy_pll_config_param *param; >> + unsigned long timeout; >> + int i, stat; >> + >> + if (res >= DW_HDMI_RES_MAX) { >> + dev_err(hdmi->dev, "Extphy can't support res %d\n", res); >> + return -EINVAL; >> + } >> + >> + /* Find out the extphy MPLL configure parameters */ >> + for (i = 0; mpll[i].mpixelclock != ~0UL; i++) >> + if (pixelclock == mpll[i].mpixelclock) >> + break; >> + if (mpll[i].mpixelclock == ~0UL) { >> + dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock); >> + return -EINVAL; >> + } >> + param = &mpll[i].param[res]; >> + >> + regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, >> + RK3229_PLL_POWER_DOWN | RK3229_PLL_PDATA_DEN); >> + >> + /* >> + * Configure external HDMI PHY PLL registers. >> + */ >> + stat = ((param->pll_nf >> 1) & EXT_PHY_PLL_FB_BIT8_MASK) | >> + ((param->vco_div_5 & 1) << 5) | >> + (param->pll_nd & EXT_PHY_PLL_PRE_DIVIDER_MASK); >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_PRE_DIVIDER); >> + >> + hdmi_extphy_write(hdmi, param->pll_nf, EXT_PHY_PLL_FB_DIVIDER); >> + >> + stat = (param->pclk_divider_a & EXT_PHY_PCLK_DIVIDERA_MASK) | >> + ((param->pclk_divider_b & 3) << 5); >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER1); >> + >> + stat = (param->pclk_divider_d & EXT_PHY_PCLK_DIVIDERD_MASK) | >> + ((param->pclk_divider_c & 3) << 5); >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_PCLK_DIVIDER2); >> + >> + stat = ((param->tmsd_divider_c & 3) << 4) | >> + ((param->tmsd_divider_a & 3) << 2) | >> + (param->tmsd_divider_b & 3); >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_TMDSCLK_DIVIDER); >> + >> + hdmi_extphy_write(hdmi, param->ppll_nf, EXT_PHY_PPLL_FB_DIVIDER); >> + >> + if (param->ppll_no == 1) { >> + hdmi_extphy_write(hdmi, 0, EXT_PHY_PPLL_POST_DIVIDER); >> + >> + stat = 0x20 | param->ppll_nd; >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER); >> + } else { >> + stat = ((param->ppll_no / 2) - 1) << 4; >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_POST_DIVIDER); >> + >> + stat = 0xe0 | param->ppll_nd; >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_PPLL_PRE_DIVIDER); >> + } >> + >> + >> + /* Find out the external HDMI PHY driver configure parameters */ >> + for (i = 0; ctrl[i].mpixelclock != ~0UL; i++) >> + if (pixelclock <= ctrl[i].mpixelclock) >> + break; >> + if (ctrl[i].mpixelclock == ~0UL) { >> + dev_err(hdmi->dev, "Extphy can't support %dHz\n", pixelclock); >> + return -EINVAL; >> + } >> + >> + /* >> + * Configure the external HDMI PHY driver registers. >> + */ >> + if (ctrl[i].slopeboost) { >> + hdmi_extphy_write(hdmi, 0xff, EXT_PHY_SIGNAL_CTRL); >> + >> + stat = (ctrl[i].slopeboost - 1) & 3; >> + stat = (stat << 6) | (stat << 4) | (stat << 2) | stat; >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_SLOPEBOOST); >> + } else >> + hdmi_extphy_write(hdmi, 0x0f, EXT_PHY_SIGNAL_CTRL); >> + >> + stat = ctrl[i].pre_emphasis & 3; >> + stat = (stat << 4) | (stat << 2) | stat; >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_PREEMPHASIS); >> + >> + stat = ((ctrl[i].clk_level & 0xf) << 4) | (ctrl[i].data2_level & 0xf); >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL1); >> + >> + stat = ((ctrl[i].data1_level & 0xf) << 4) | (ctrl[i].data0_level & 0xf); >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_LEVEL2); >> + >> + hdmi_extphy_write(hdmi, 0x22, 0xf3); >> + >> + stat = clk_get_rate(hdmi->extphy_pclk) / 100000; >> + hdmi_extphy_write(hdmi, ((stat >> 8) & 0xff) | 0x80, EXT_PHY_TERM_CAL); >> + hdmi_extphy_write(hdmi, stat & 0xff, EXT_PHY_TERM_CAL_DIV_L); >> + >> + if (pixelclock > 340000000) >> + stat = EXT_PHY_AUTO_R100_OHMS; >> + else if (pixelclock > 200000000) >> + stat = EXT_PHY_AUTO_R50_OHMS; >> + else >> + stat = EXT_PHY_AUTO_ROPEN_CIRCUIT; >> + hdmi_extphy_write(hdmi, stat | 0x20, EXT_PHY_TERM_RESIS_AUTO); >> + hdmi_extphy_write(hdmi, (stat >> 8) & 0xff, EXT_PHY_TERM_CAL); >> + >> + stat = (pixelclock > 200000000) ? 0 : 0x11; >> + hdmi_extphy_write(hdmi, stat, EXT_PHY_PLL_BW); >> + hdmi_extphy_write(hdmi, 0x27, EXT_PHY_PPLL_BW); >> + >> + regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_POWER_UP); >> + >> + /* Detect whether PLL is lock or not */ >> + timeout = jiffies + msecs_to_jiffies(100); >> + while (!time_after(jiffies, timeout)) { >> + usleep_range(1000, 2000); >> + stat = hdmi_extphy_read(hdmi, EXT_PHY_PPLL_POST_DIVIDER); >> + if (stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) >> + break; >> + } >> + >> + regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, RK3229_PLL_PDATA_EN); >> + >> + if ((stat & EXT_PHY_PPLL_LOCK_STATUS_MASK) == 0) { >> + dev_err(hdmi->dev, "EXT PHY PLL not locked\n"); >> + return -EBUSY; >> + } >> + >> + return 0; >> +} >> + >> static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) >> { >> struct device_node *np = hdmi->dev->of_node; >> + struct platform_device *pdev; >> + struct resource *iores; >> + int ret; >> + >> + pdev = container_of(hdmi->dev, struct platform_device, dev); >> >> hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); >> if (IS_ERR(hdmi->regmap)) { >> @@ -152,6 +424,37 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) >> return PTR_ERR(hdmi->regmap); >> } >> >> + if (hdmi->plat_data.dev_type == RK3229_HDMI) { >> + iores = platform_get_resource(pdev, IORESOURCE_MEM, 1); >> + if (!iores) >> + return -ENXIO; >> + >> + hdmi->extphy_regbase = devm_ioremap_resource(hdmi->dev, iores); >> + if (IS_ERR(hdmi->extphy_regbase)) { >> + dev_err(hdmi->dev, "failed to map extphy regbase\n"); >> + return PTR_ERR(hdmi->extphy_regbase); >> + } >> + >> + hdmi->extphy_pclk = devm_clk_get(hdmi->dev, "extphy"); >> + if (IS_ERR(hdmi->extphy_pclk)) { >> + dev_err(hdmi->dev, "failed to get extphy clock\n"); >> + return PTR_ERR(hdmi->extphy_pclk); >> + } >> + >> + ret = clk_prepare_enable(hdmi->extphy_pclk); >> + if (ret) { >> + dev_err(hdmi->dev, "failed to enable extphy clk: %d\n", >> + ret); >> + return ret; >> + } >> + >> + regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON6, >> + RK3229_IO_3V_DOMAIN); >> + >> + regmap_write(hdmi->regmap, RK3229_GRF_SOC_CON2, >> + RK3229_DDC_MASK_EN); >> + } >> + >> return 0; >> } >> >> @@ -159,17 +462,23 @@ static enum drm_mode_status >> dw_hdmi_rockchip_mode_valid(const struct dw_hdmi_plat_data *plat_data, >> struct drm_display_mode *mode) >> { >> - const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; >> int pclk = mode->clock * 1000; >> bool valid = false; >> int i; >> >> - for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) { >> - if (pclk == mpll_cfg[i].mpixelclock) { >> - valid = true; >> - break; >> - } >> - } >> + if (plat_data->dev_type == RK3288_HDMI) >> + for (i = 0; rockchip_mpll_cfg[i].mpixelclock != ~0UL; i++) >> + if (pclk == rockchip_mpll_cfg[i].mpixelclock) { >> + valid = true; >> + break; >> + } >> + >> + if (plat_data->dev_type == RK3229_HDMI) >> + for (i = 0; rockchip_extphy_pll_cfg[i].mpixelclock != ~0UL; i++) >> + if (pclk == rockchip_extphy_pll_cfg[i].mpixelclock) { >> + valid = true; >> + break; >> + } >> >> return (valid) ? MODE_OK : MODE_BAD; >> } >> @@ -199,21 +508,30 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, >> static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) >> { >> struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); >> + int out_mode = ROCKCHIP_OUT_MODE_AAAA; >> u32 val; >> int mux; >> >> + if (hdmi->plat_data.dev_type == RK3229_HDMI) >> + out_mode = ROCKCHIP_OUT_MODE_P888; >> + >> rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA, >> - ROCKCHIP_OUT_MODE_AAAA); >> + out_mode); >> >> - mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); >> - if (mux) >> - val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16); >> - else >> - val = HDMI_SEL_VOP_LIT << 16; >> + if (hdmi->plat_data.dev_type == RK3288_HDMI) { >> + mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, >> + encoder); >> + if (mux) >> + val = RK3288_HDMI_SEL_VOP_LIT | >> + (RK3288_HDMI_SEL_VOP_LIT << 16); >> + else >> + val = RK3288_HDMI_SEL_VOP_LIT << 16; >> >> - regmap_write(hdmi->regmap, GRF_SOC_CON6, val); >> - dev_dbg(hdmi->dev, "vop %s output to hdmi\n", >> - (mux) ? "LIT" : "BIG"); >> + regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON6, val); >> + >> + dev_dbg(hdmi->dev, "vop %s output to hdmi\n", >> + (mux) ? "LIT" : "BIG"); >> + } >> } >> >> static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { >> @@ -223,7 +541,7 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun >> .disable = dw_hdmi_rockchip_encoder_disable, >> }; >> >> -static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { >> +static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = { >> .mode_valid = dw_hdmi_rockchip_mode_valid, >> .mpll_cfg = rockchip_mpll_cfg, >> .cur_ctr = rockchip_cur_ctr, >> @@ -231,9 +549,18 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { >> .dev_type = RK3288_HDMI, >> }; >> >> +static const struct dw_hdmi_plat_data rk3229_hdmi_drv_data = { >> + .mode_valid = dw_hdmi_rockchip_mode_valid, >> + .extphy_config = rockchip_extphy_config, >> + .dev_type = RK3229_HDMI, >> +}; >> + >> static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { >> { .compatible = "rockchip,rk3288-dw-hdmi", >> - .data = &rockchip_hdmi_drv_data >> + .data = &rk3288_hdmi_drv_data >> + }, >> + { .compatible = "rockchip,rk3229-dw-hdmi", >> + .data = &rk3229_hdmi_drv_data >> }, >> {}, >> }; >> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h >> new file mode 100644 >> index 0000000..f7ec733 >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.h >> @@ -0,0 +1,137 @@ >> +#ifndef __DW_HDMI_ROCKCHIP__ >> +#define __DW_HDMI_ROCKCHIP__ >> + >> +struct extphy_config_tab { >> + u32 mpixelclock; >> + int pre_emphasis; >> + int slopeboost; >> + int clk_level; >> + int data0_level; >> + int data1_level; >> + int data2_level; >> +}; >> + >> +struct extphy_pll_config_tab { >> + unsigned long mpixelclock; >> + struct extphy_pll_config_param { >> + u8 pll_nd; >> + u16 pll_nf; >> + u8 tmsd_divider_a; >> + u8 tmsd_divider_b; >> + u8 tmsd_divider_c; >> + u8 pclk_divider_a; >> + u8 pclk_divider_b; >> + u8 pclk_divider_c; >> + u8 pclk_divider_d; >> + u8 vco_div_5; >> + u8 ppll_nd; >> + u8 ppll_nf; >> + u8 ppll_no; >> + } param[DW_HDMI_RES_MAX]; >> +}; >> + >> +#define RK3288_GRF_SOC_CON6 0x025c >> +#define RK3288_HDMI_SEL_VOP_LIT (1 << 4) >> + >> +#define RK3229_GRF_SOC_CON6 0x0418 >> +#define RK3229_IO_3V_DOMAIN ((7 << 4) | (7 << (4 + 16))) >> + >> +#define RK3229_GRF_SOC_CON2 0x0408 >> +#define RK3229_DDC_MASK_EN ((3 << 13) | (3 << (13 + 16))) >> + >> +#define RK3229_PLL_POWER_DOWN (BIT(12) | BIT(12 + 16)) >> +#define RK3229_PLL_POWER_UP BIT(12 + 16) >> +#define RK3229_PLL_PDATA_DEN BIT(11 + 16) >> +#define RK3229_PLL_PDATA_EN (BIT(11) | BIT(11 + 16)) >> + >> +/* PHY Defined for RK322X */ >> +#define EXT_PHY_CONTROL 0 >> +#define EXT_PHY_ANALOG_RESET_MASK 0x80 >> +#define EXT_PHY_DIGITAL_RESET_MASK 0x40 >> +#define EXT_PHY_PCLK_INVERT_MASK 0x08 >> +#define EXT_PHY_PREPCLK_INVERT_MASK 0x04 >> +#define EXT_PHY_TMDSCLK_INVERT_MASK 0x02 >> +#define EXT_PHY_SRC_SELECT_MASK 0x01 >> + >> +#define EXT_PHY_TERM_CAL 0x03 >> +#define EXT_PHY_TERM_CAL_EN_MASK 0x80 >> +#define EXT_PHY_TERM_CAL_DIV_H_MASK 0x7f >> + >> +#define EXT_PHY_TERM_CAL_DIV_L 0x04 >> + >> +#define EXT_PHY_PLL_PRE_DIVIDER 0xe2 >> +#define EXT_PHY_PLL_FB_BIT8_MASK 0x80 >> +#define EXT_PHY_PLL_PCLK_DIV5_EN_MASK 0x20 >> +#define EXT_PHY_PLL_PRE_DIVIDER_MASK 0x1f >> + >> +#define EXT_PHY_PLL_FB_DIVIDER 0xe3 >> + >> +#define EXT_PHY_PCLK_DIVIDER1 0xe4 >> +#define EXT_PHY_PCLK_DIVIDERB_MASK 0x60 >> +#define EXT_PHY_PCLK_DIVIDERA_MASK 0x1f >> + >> +#define EXT_PHY_PCLK_DIVIDER2 0xe5 >> +#define EXT_PHY_PCLK_DIVIDERC_MASK 0x60 >> +#define EXT_PHY_PCLK_DIVIDERD_MASK 0x1f >> + >> +#define EXT_PHY_TMDSCLK_DIVIDER 0xe6 >> +#define EXT_PHY_TMDSCLK_DIVIDERC_MASK 0x30 >> +#define EXT_PHY_TMDSCLK_DIVIDERA_MASK 0x0c >> +#define EXT_PHY_TMDSCLK_DIVIDERB_MASK 0x03 >> + >> +#define EXT_PHY_PLL_BW 0xe7 >> + >> +#define EXT_PHY_PPLL_PRE_DIVIDER 0xe9 >> +#define EXT_PHY_PPLL_ENABLE_MASK 0xc0 >> +#define EXT_PHY_PPLL_PRE_DIVIDER_MASK 0x1f >> + >> +#define EXT_PHY_PPLL_FB_DIVIDER 0xea >> + >> +#define EXT_PHY_PPLL_POST_DIVIDER 0xeb >> +#define EXT_PHY_PPLL_FB_DIVIDER_BIT8_MASK 0x80 >> +#define EXT_PHY_PPLL_POST_DIVIDER_MASK 0x30 >> +#define EXT_PHY_PPLL_LOCK_STATUS_MASK 0x01 >> + >> +#define EXT_PHY_PPLL_BW 0xec >> + >> +#define EXT_PHY_SIGNAL_CTRL 0xee >> +#define EXT_PHY_TRANSITION_CLK_EN_MASK 0x80 >> +#define EXT_PHY_TRANSITION_D0_EN_MASK 0x40 >> +#define EXT_PHY_TRANSITION_D1_EN_MASK 0x20 >> +#define EXT_PHY_TRANSITION_D2_EN_MASK 0x10 >> +#define EXT_PHY_LEVEL_CLK_EN_MASK 0x08 >> +#define EXT_PHY_LEVEL_D0_EN_MASK 0x04 >> +#define EXT_PHY_LEVEL_D1_EN_MASK 0x02 >> +#define EXT_PHY_LEVEL_D2_EN_MASK 0x01 >> + >> +#define EXT_PHY_SLOPEBOOST 0xef >> +#define EXT_PHY_SLOPEBOOST_CLK_MASK 0x03 >> +#define EXT_PHY_SLOPEBOOST_D0_MASK 0x0c >> +#define EXT_PHY_SLOPEBOOST_D1_MASK 0x30 >> +#define EXT_PHY_SLOPEBOOST_D2_MASK 0xc0 >> + >> +#define EXT_PHY_PREEMPHASIS 0xf0 >> +#define EXT_PHY_PREEMPHASIS_D0_MASK 0x03 >> +#define EXT_PHY_PREEMPHASIS_D1_MASK 0x0c >> +#define EXT_PHY_PREEMPHASIS_D2_MASK 0x30 >> + >> +#define EXT_PHY_LEVEL1 0xf1 >> +#define EXT_PHY_LEVEL_CLK_MASK 0xf0 >> +#define EXT_PHY_LEVEL_D2_MASK 0x0f >> + >> +#define EXT_PHY_LEVEL2 0xf2 >> +#define EXT_PHY_LEVEL_D1_MASK 0xf0 >> +#define EXT_PHY_LEVEL_D0_MASK 0x0f >> + >> +#define EXT_PHY_TERM_RESIS_AUTO 0xf4 >> +#define EXT_PHY_AUTO_R50_OHMS 0 >> +#define EXT_PHY_AUTO_R75_OHMS (1 << 2) >> +#define EXT_PHY_AUTO_R100_OHMS (2 << 2) >> +#define EXT_PHY_AUTO_ROPEN_CIRCUIT (3 << 2) >> + >> +#define EXT_PHY_TERM_RESIS_MANUAL_CLK 0xfb >> +#define EXT_PHY_TERM_RESIS_MANUAL_D2 0xfc >> +#define EXT_PHY_TERM_RESIS_MANUAL_D1 0xfd >> +#define EXT_PHY_TERM_RESIS_MANUAL_D0 0xfe >> + >> +#endif /* __DW_HDMI_ROCKCHIP__ */ >> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h >> index f8dec64..4e63158 100644 >> --- a/include/drm/bridge/dw_hdmi.h >> +++ b/include/drm/bridge/dw_hdmi.h >> @@ -24,6 +24,7 @@ enum { >> enum dw_hdmi_devtype { >> IMX6Q_HDMI, >> IMX6DL_HDMI, >> + RK3229_HDMI, >> RK3288_HDMI, >> }; >> >> @@ -54,6 +55,8 @@ struct dw_hdmi_plat_data { >> const struct dw_hdmi_phy_config *phy_config; >> enum drm_mode_status (*mode_valid)(const struct dw_hdmi_plat_data *pd, >> struct drm_display_mode *mode); >> + int (*extphy_config)(const struct dw_hdmi_plat_data *plat_data, >> + int res_idx, int pixelclock); >> }; >> >> void dw_hdmi_unbind(struct device *dev, struct device *master, void *data); > > > >