drm: bridge: dw-hdmi: set ddc scl clock rate according to dts
authorZheng Yang <zhengyang@rock-chips.com>
Tue, 28 Mar 2017 10:28:16 +0000 (18:28 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Fri, 31 Mar 2017 02:20:45 +0000 (10:20 +0800)
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 <zhengyang@rock-chips.com>
drivers/gpu/drm/bridge/dw-hdmi.c

index a6e5dfe..5df2515 100644 (file)
@@ -176,6 +176,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 {
@@ -281,6 +284,49 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
        hdmi_modb(hdmi, data << shift, mask, reg);
 }
 
+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)
 {
        /* Software reset */
@@ -302,6 +348,8 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
        /* Mute DONE and ERROR interrupts */
        hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
                    HDMI_IH_MUTE_I2CM_STAT0);
+
+       dw_hdmi_i2c_set_divs(hdmi);
 }
 
 static int dw_hdmi_i2c_read(struct dw_hdmi *hdmi,
@@ -2401,6 +2449,16 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
                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;
        }
 
        hdmi->regs = devm_ioremap_resource(dev, iores);