diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index 67ffb23faecb..350a467c66c0 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -47,6 +47,22 @@ struct inno_hdmi_i2c { struct completion cmp; }; +enum inno_hdmi_dev_type { + RK3036_HDMI, + RK3128_HDMI, +}; + +struct inno_hdmi_phy_config { + unsigned long mpixelclock; + u8 pre_emphasis; /* pre-emphasis value */ + u8 vlev_ctr; /* voltage level control */ +}; + +struct inno_hdmi_plat_data { + enum inno_hdmi_dev_type dev_type; + struct inno_hdmi_phy_config *phy_config; +}; + struct inno_hdmi { struct device *dev; struct drm_device *drm_dev; @@ -62,6 +78,7 @@ struct inno_hdmi { struct i2c_adapter *ddc; unsigned int tmds_rate; + const struct inno_hdmi_plat_data *plat_data; struct hdmi_data_info hdmi_data; struct drm_display_mode previous_mode; @@ -189,12 +206,20 @@ static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable) static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode) { + const struct inno_hdmi_phy_config *phy_config = + hdmi->plat_data->phy_config; + switch (mode) { case NORMAL: inno_hdmi_sys_power(hdmi, false); - - hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f); - hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb); + for (; phy_config->mpixelclock != ~0UL; phy_config++) + if (hdmi->tmds_rate <= phy_config->mpixelclock) + break; + if (!phy_config->mpixelclock) + return; + hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, + phy_config->pre_emphasis); + hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->vlev_ctr); hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15); hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14); @@ -383,6 +408,12 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi, { int value; + if (hdmi->plat_data->dev_type == RK3036_HDMI) { + value = BIT(20) | BIT(21); + value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? BIT(4) : 0; + value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? BIT(5) : 0; + hdmi_writeb(hdmi, 0x148, value); + } /* Set detail external video timing polarity and interlace mode */ value = v_EXTERANL_VIDEO(1); value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? @@ -804,6 +835,41 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi) return adap; } +static struct inno_hdmi_phy_config rk3036_hdmi_phy_config[] = { + /* pixelclk pre-emp vlev */ + { 74250000, 0x3f, 0xbb }, + { 165000000, 0x6f, 0xbb }, + { ~0UL, 0x00, 0x00 } +}; + +static struct inno_hdmi_phy_config rk3128_hdmi_phy_config[] = { + /* pixelclk pre-emp vlev */ + { 74250000, 0x3f, 0xaa }, + { 165000000, 0x5f, 0xaa }, + { ~0UL, 0x00, 0x00 } +}; + +static const struct inno_hdmi_plat_data rk3036_hdmi_drv_data = { + .dev_type = RK3036_HDMI, + .phy_config = rk3036_hdmi_phy_config, +}; + +static const struct inno_hdmi_plat_data rk3128_hdmi_drv_data = { + .dev_type = RK3128_HDMI, + .phy_config = rk3128_hdmi_phy_config, +}; + +static const struct of_device_id inno_hdmi_dt_ids[] = { + { .compatible = "rockchip,rk3036-inno-hdmi", + .data = &rk3036_hdmi_drv_data, + }, + { .compatible = "rockchip,rk3128-inno-hdmi", + .data = &rk3128_hdmi_drv_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids); + static int inno_hdmi_bind(struct device *dev, struct device *master, void *data) { @@ -820,6 +886,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, hdmi->dev = dev; hdmi->drm_dev = drm; + hdmi->plat_data = device_get_match_data(hdmi->dev); iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); hdmi->regs = devm_ioremap_resource(dev, iores); @@ -918,13 +985,6 @@ static int inno_hdmi_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id inno_hdmi_dt_ids[] = { - { .compatible = "rockchip,rk3036-inno-hdmi", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids); - struct platform_driver inno_hdmi_driver = { .probe = inno_hdmi_probe, .remove = inno_hdmi_remove,