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 <knaerz...@gmail.com>
---
changes in v2:
 - no changes

changes in v3:
 - adapt to the newly introduced inno_hdmi_power_up / inno_hdmi_standby 
functions

changes in v4:
 - none

 drivers/gpu/drm/rockchip/inno_hdmi.c | 74 ++++++++++++++++++++++++++--
 1 file changed, 69 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c 
b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 44c311e7e2d3..34f0358e8d7b 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -26,6 +26,17 @@
 
 #include "inno_hdmi.h"
 
+struct inno_hdmi_phy_config {
+       unsigned long 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 inno_hdmi_i2c {
        struct i2c_adapter adap;
 
@@ -47,6 +58,8 @@ struct inno_hdmi {
 
        struct inno_hdmi_i2c *i2c;
        struct i2c_adapter *ddc;
+
+       const struct inno_hdmi_variant *variant;
 };
 
 struct inno_hdmi_connector_state {
@@ -113,6 +126,30 @@ static const char coeff_csc[][24] = {
        },
 };
 
+static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = {
+       {  74250000, 0x3f, 0xbb },
+       { 165000000, 0x6f, 0xbb },
+       {      ~0UL, 0x00, 0x00 }
+};
+
+static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi,
+                                    unsigned long 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 %lu\n",
+                     pixelclk);
+
+       return -EINVAL;
+}
+
 static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
 {
        return readl_relaxed(hdmi->regs + (offset) * 0x04);
@@ -164,12 +201,25 @@ static void inno_hdmi_standby(struct inno_hdmi *hdmi)
        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
 };
 
-static void inno_hdmi_power_up(struct inno_hdmi *hdmi)
+static void inno_hdmi_power_up(struct inno_hdmi *hdmi,
+                              unsigned long mpixelclock)
 {
+       struct inno_hdmi_phy_config *phy_config;
+       int ret = inno_hdmi_find_phy_config(hdmi, mpixelclock);
+
+       if (ret < 0) {
+               phy_config = hdmi->variant->default_phy_config;
+               DRM_DEV_ERROR(hdmi->dev,
+                             "Using default phy configuration for TMDS rate 
%lu",
+                             mpixelclock);
+       } else {
+               phy_config = &hdmi->variant->phy_configs[ret];
+       }
+
        inno_hdmi_sys_power(hdmi, false);
 
-       hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
-       hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+       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);
        hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
@@ -406,6 +456,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
                           struct drm_display_mode *mode)
 {
        struct drm_display_info *display = &hdmi->connector.display_info;
+       unsigned long mpixelclock = mode->clock * 1000;
 
        /* Mute video and audio output */
        hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
@@ -428,13 +479,13 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
         * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
         * clock rate, and reconfigure the DDC clock.
         */
-       inno_hdmi_i2c_init(hdmi, mode->clock * 1000);
+       inno_hdmi_i2c_init(hdmi, mpixelclock);
 
        /* Unmute video and audio output */
        hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
                  v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
 
-       inno_hdmi_power_up(hdmi);
+       inno_hdmi_power_up(hdmi, mpixelclock);
 
        return 0;
 }
@@ -827,6 +878,7 @@ 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;
 
@@ -836,6 +888,12 @@ static int inno_hdmi_bind(struct device *dev, struct 
device *master,
 
        hdmi->dev = dev;
 
+       variant = of_device_get_match_data(hdmi->dev);
+       if (!variant)
+               return -EINVAL;
+
+       hdmi->variant = variant;
+
        hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(hdmi->regs))
                return PTR_ERR(hdmi->regs);
@@ -929,8 +987,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

Reply via email to