From caa9fa75d65a67d0439c80f7f8fdc906c39f8ef7 Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Tue, 28 Mar 2017 18:28:16 +0800 Subject: [PATCH] drm: bridge: dw-hdmi: set ddc scl clock rate according to dts To set dw hdmi i2c bus adapter scl clock rate, we introduce two device tree parameter, ddc-i2c-scl-high-time-ns and ddc-i2c-scl-low-time-ns. ddc-i2c-scl-high-time-ns: how many ns SCL hold high ddc-i2c-scl-low-time-ns: how many ns SCL hold low After measurement, 50KHz scl clock rate recommended configuration is: &hdmi { ddc-i2c-scl-high-time-ns = <9625>; ddc-i2c-scl-low-time-ns = <10000>; }; 100KHz recommended configuration is: &hdmi { ddc-i2c-scl-high-time-ns = <4708>; ddc-i2c-scl-low-time-ns = <4916>; }; If dts parameter is not available, the default scl rate is 100KHz. Change-Id: I6f6b0bf1694ab59e70da789ead99e15a53c93e4d Signed-off-by: Zheng Yang --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 64 ++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index e164868ae561..7995f492f4fa 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -194,6 +194,9 @@ struct dw_hdmi_i2c { u8 slave_reg; bool is_regaddr; bool is_segment; + + unsigned int scl_high_ns; + unsigned int scl_low_ns; }; struct dw_hdmi_phy_data { @@ -380,6 +383,49 @@ static void init_hpd_work(struct dw_hdmi *hdmi) INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event); } +static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi) +{ + unsigned long clk_rate_khz; + unsigned long low_ns, high_ns; + unsigned long div_low, div_high; + + /* Standard-mode */ + if (hdmi->i2c->scl_high_ns < 4000) + high_ns = 4708; + else + high_ns = hdmi->i2c->scl_high_ns; + + if (hdmi->i2c->scl_low_ns < 4700) + low_ns = 4916; + else + low_ns = hdmi->i2c->scl_low_ns; + + /* Adjust to avoid overflow */ + clk_rate_khz = DIV_ROUND_UP(clk_get_rate(hdmi->isfr_clk), 1000); + + div_low = (clk_rate_khz * low_ns) / 1000000; + if ((clk_rate_khz * low_ns) % 1000000) + div_low++; + + div_high = (clk_rate_khz * high_ns) / 1000000; + if ((clk_rate_khz * high_ns) % 1000000) + div_high++; + + /* Maximum divider supported by hw is 0xffff */ + if (div_low > 0xffff) + div_low = 0xffff; + + if (div_high > 0xffff) + div_high = 0xffff; + + hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR); + hdmi_writeb(hdmi, (div_high >> 8) & 0xff, + HDMI_I2CM_SS_SCL_HCNT_1_ADDR); + hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR); + hdmi_writeb(hdmi, (div_low >> 8) & 0xff, + HDMI_I2CM_SS_SCL_LCNT_1_ADDR); +} + static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) { hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, @@ -410,6 +456,8 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi) /* set SDA high level holding time */ hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD); + + dw_hdmi_i2c_set_divs(hdmi); } static bool dw_hdmi_i2c_unwedge(struct dw_hdmi *hdmi) @@ -3577,7 +3625,8 @@ static void dw_hdmi_init_hw(struct dw_hdmi *hdmi) * Even if we are using a separate i2c adapter doing this doesn't * hurt. */ - dw_hdmi_i2c_init(hdmi); + if (hdmi->i2c) + dw_hdmi_i2c_init(hdmi); if (hdmi->phy.ops->setup_hpd) hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); @@ -3906,7 +3955,6 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, } init_hpd_work(hdmi); - dw_hdmi_init_hw(hdmi); irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -3950,8 +3998,20 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, hdmi->ddc = dw_hdmi_i2c_adapter(hdmi); if (IS_ERR(hdmi->ddc)) hdmi->ddc = NULL; + /* + * Read high and low time from device tree. If not available use + * the default timing scl clock rate is about 99.6KHz. + */ + if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns", + &hdmi->i2c->scl_high_ns)) + hdmi->i2c->scl_high_ns = 4708; + if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns", + &hdmi->i2c->scl_low_ns)) + hdmi->i2c->scl_low_ns = 4916; } + dw_hdmi_init_hw(hdmi); + hdmi->bridge.driver_private = hdmi; hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID