In preparation to support RK3128's integration of the controller, this patch adds a simple variant implementation. They mainly differ in the phy configuration required, so those are part of the match_data. The values have been taken from downstream. The pixelclocks in there are meant to be max-inclusive. Signed-off-by: Alex Bee <knaerzche@xxxxxxxxx> --- drivers/gpu/drm/rockchip/inno_hdmi.c | 68 +++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index 32626a75723c..593b184bd0ad 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -27,6 +27,17 @@ #include "inno_hdmi.h" +struct inno_hdmi_phy_config { + unsigned int pixelclock; + u8 pre_emphasis; + u8 voltage_level_control; +}; + +struct inno_hdmi_variant { + struct inno_hdmi_phy_config *phy_configs; + struct inno_hdmi_phy_config *default_phy_config; +}; + struct hdmi_data_info { int vic; bool sink_has_audio; @@ -64,6 +75,14 @@ struct inno_hdmi { struct hdmi_data_info hdmi_data; struct drm_display_mode previous_mode; + + const struct inno_hdmi_variant *variant; +}; + +static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = { + { 74250000, 0x3f, 0xbb }, + { 165000000, 0x6f, 0xbb }, + { ~0UL, 0x00, 0x00 } }; static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder) @@ -176,6 +195,24 @@ static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset, hdmi_writeb(hdmi, offset, temp); } +static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi, + unsigned int pixelclk) +{ + const struct inno_hdmi_phy_config *phy_configs = + hdmi->variant->phy_configs; + int i; + + for (i = 0; phy_configs[i].pixelclock != ~0UL; i++) { + if (pixelclk <= phy_configs[i].pixelclock) + return i; + } + + DRM_DEV_DEBUG(hdmi->dev, "No phy configuration for pixelclock %u\n", + pixelclk); + + return -EINVAL; +} + static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi) { int ddc_bus_freq; @@ -200,12 +237,25 @@ 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) { + int ret; + struct inno_hdmi_phy_config *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); + ret = inno_hdmi_find_phy_config(hdmi, hdmi->tmds_rate); + if (ret < 0) { + phy_config = hdmi->variant->default_phy_config; + DRM_DEV_ERROR(hdmi->dev, + "Using default phy configuration for TMDS rate %u", + hdmi->tmds_rate); + } else { + phy_config = &hdmi->variant->phy_configs[ret]; + } + + hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, phy_config->pre_emphasis); + hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->voltage_level_control); hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15); hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14); @@ -845,6 +895,8 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = data; struct inno_hdmi *hdmi; + const struct inno_hdmi_variant *variant; + int irq; int ret; @@ -853,6 +905,12 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, return -ENOMEM; hdmi->dev = dev; + + variant = of_device_get_match_data(hdmi->dev); + if (!variant) + return -EINVAL; + + hdmi->variant = variant; hdmi->drm_dev = drm; hdmi->regs = devm_platform_ioremap_resource(pdev, 0); @@ -949,8 +1007,14 @@ static void inno_hdmi_remove(struct platform_device *pdev) component_del(&pdev->dev, &inno_hdmi_ops); } +static const struct inno_hdmi_variant rk3036_inno_hdmi_variant = { + .phy_configs = rk3036_hdmi_phy_configs, + .default_phy_config = &rk3036_hdmi_phy_configs[1], +}; + static const struct of_device_id inno_hdmi_dt_ids[] = { { .compatible = "rockchip,rk3036-inno-hdmi", + .data = &rk3036_inno_hdmi_variant, }, {}, }; -- 2.43.0