drm: bridge: dw-hdmi: set ddc scl clock rate according to dts
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / bridge / dw-hdmi.c
index a6e5dfe3f1ceaf7ad041f559885a9c96cc600d3e..5df2515583f7172dea83881e1152faea39014884 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);