Merge tag 'ti-v2020.07-rc3' of https://gitlab.denx.de/u-boot/custodians/u-boot-ti
[oweals/u-boot.git] / drivers / video / sunxi / sunxi_dw_hdmi.c
index 9dbea649a0ff709da88a28c7859f9c9b60511f11..01d4b7a11c96026a87b3b271cc8ea4cd431945b1 100644 (file)
 #include <dm.h>
 #include <dw_hdmi.h>
 #include <edid.h>
+#include <log.h>
+#include <time.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/lcdc.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
 
 struct sunxi_dw_hdmi_priv {
        struct dw_hdmi hdmi;
@@ -132,7 +136,7 @@ static int sunxi_dw_hdmi_wait_for_hpd(void)
        return -1;
 }
 
-static void sunxi_dw_hdmi_phy_set(uint clock)
+static void sunxi_dw_hdmi_phy_set(uint clock, int phy_div)
 {
        struct sunxi_hdmi_phy * const phy =
                (struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS);
@@ -146,7 +150,7 @@ static void sunxi_dw_hdmi_phy_set(uint clock)
        switch (div) {
        case 1:
                writel(0x30dc5fc0, &phy->pll);
-               writel(0x800863C0, &phy->clk);
+               writel(0x800863C0 | (phy_div - 1), &phy->clk);
                mdelay(10);
                writel(0x00000001, &phy->unk3);
                setbits_le32(&phy->pll, BIT(25));
@@ -164,7 +168,7 @@ static void sunxi_dw_hdmi_phy_set(uint clock)
                break;
        case 2:
                writel(0x39dc5040, &phy->pll);
-               writel(0x80084381, &phy->clk);
+               writel(0x80084380 | (phy_div - 1), &phy->clk);
                mdelay(10);
                writel(0x00000001, &phy->unk3);
                setbits_le32(&phy->pll, BIT(25));
@@ -178,7 +182,7 @@ static void sunxi_dw_hdmi_phy_set(uint clock)
                break;
        case 4:
                writel(0x39dc5040, &phy->pll);
-               writel(0x80084343, &phy->clk);
+               writel(0x80084340 | (phy_div - 1), &phy->clk);
                mdelay(10);
                writel(0x00000001, &phy->unk3);
                setbits_le32(&phy->pll, BIT(25));
@@ -192,7 +196,7 @@ static void sunxi_dw_hdmi_phy_set(uint clock)
                break;
        case 11:
                writel(0x39dc5040, &phy->pll);
-               writel(0x8008430a, &phy->clk);
+               writel(0x80084300 | (phy_div - 1), &phy->clk);
                mdelay(10);
                writel(0x00000001, &phy->unk3);
                setbits_le32(&phy->pll, BIT(25));
@@ -207,36 +211,46 @@ static void sunxi_dw_hdmi_phy_set(uint clock)
        }
 }
 
-static void sunxi_dw_hdmi_pll_set(uint clk_khz)
+static void sunxi_dw_hdmi_pll_set(uint clk_khz, int *phy_div)
 {
-       int value, n, m, div = 0, diff;
-       int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF;
-
-       div = sunxi_dw_hdmi_get_divider(clk_khz * 1000);
+       int value, n, m, div, diff;
+       int best_n = 0, best_m = 0, best_div = 0, best_diff = 0x0FFFFFFF;
 
        /*
         * Find the lowest divider resulting in a matching clock. If there
         * is no match, pick the closest lower clock, as monitors tend to
         * not sync to higher frequencies.
         */
-       for (m = 1; m <= 16; m++) {
-               n = (m * div * clk_khz) / 24000;
-
-               if ((n >= 1) && (n <= 128)) {
-                       value = (24000 * n) / m / div;
-                       diff = clk_khz - value;
-                       if (diff < best_diff) {
-                               best_diff = diff;
-                               best_m = m;
-                               best_n = n;
+       for (div = 1; div <= 16; div++) {
+               int target = clk_khz * div;
+
+               if (target < 192000)
+                       continue;
+               if (target > 912000)
+                       continue;
+
+               for (m = 1; m <= 16; m++) {
+                       n = (m * target) / 24000;
+
+                       if (n >= 1 && n <= 128) {
+                               value = (24000 * n) / m / div;
+                               diff = clk_khz - value;
+                               if (diff < best_diff) {
+                                       best_diff = diff;
+                                       best_m = m;
+                                       best_n = n;
+                                       best_div = div;
+                               }
                        }
                }
        }
 
+       *phy_div = best_div;
+
        clock_set_pll3_factors(best_m, best_n);
        debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
-             clk_khz, (clock_get_pll3() / 1000) / div,
-             best_n, best_m, div);
+             clk_khz, (clock_get_pll3() / 1000) / best_div,
+             best_n, best_m, best_div);
 }
 
 static void sunxi_dw_hdmi_lcdc_init(int mux, const struct display_timing *edid,
@@ -244,7 +258,7 @@ static void sunxi_dw_hdmi_lcdc_init(int mux, const struct display_timing *edid,
 {
        struct sunxi_ccm_reg * const ccm =
                (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
-       int div = sunxi_dw_hdmi_get_divider(edid->pixelclock.typ);
+       int div = DIV_ROUND_UP(clock_get_pll3(), edid->pixelclock.typ);
        struct sunxi_lcdc_reg *lcdc;
 
        if (mux == 0) {
@@ -276,8 +290,10 @@ static void sunxi_dw_hdmi_lcdc_init(int mux, const struct display_timing *edid,
 
 static int sunxi_dw_hdmi_phy_cfg(struct dw_hdmi *hdmi, uint mpixelclock)
 {
-       sunxi_dw_hdmi_pll_set(mpixelclock/1000);
-       sunxi_dw_hdmi_phy_set(mpixelclock);
+       int phy_div;
+
+       sunxi_dw_hdmi_pll_set(mpixelclock / 1000, &phy_div);
+       sunxi_dw_hdmi_phy_set(mpixelclock, phy_div);
 
        return 0;
 }
@@ -361,6 +377,9 @@ static int sunxi_dw_hdmi_probe(struct udevice *dev)
        priv->hdmi.phy_set = sunxi_dw_hdmi_phy_cfg;
        priv->mux = uc_plat->source_id;
 
+       uclass_get_device_by_phandle(UCLASS_I2C, dev, "ddc-i2c-bus",
+                                    &priv->hdmi.ddc_bus);
+
        dw_hdmi_init(&priv->hdmi);
 
        return 0;