diff options
Diffstat (limited to 'drivers/video/sunxi')
-rw-r--r-- | drivers/video/sunxi/sunxi_dw_hdmi.c | 62 |
1 files changed, 37 insertions, 25 deletions
diff --git a/drivers/video/sunxi/sunxi_dw_hdmi.c b/drivers/video/sunxi/sunxi_dw_hdmi.c index 9dbea649a0..6fe1aa7ee4 100644 --- a/drivers/video/sunxi/sunxi_dw_hdmi.c +++ b/drivers/video/sunxi/sunxi_dw_hdmi.c @@ -132,7 +132,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 +146,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 +164,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 +178,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 +192,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 +207,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 +254,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 = clock_get_pll3() / edid->pixelclock.typ; struct sunxi_lcdc_reg *lcdc; if (mux == 0) { @@ -276,8 +286,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; } |