sunxi HDMI clock fix

-----BEGIN PGP SIGNATURE-----
 
 iGwEABECACwWIQSC4hxrSoIUVfFO0kRM6ATMmsalXAUCXJ6OeQ4cYWd1c3RAZGVu
 eC5kZQAKCRBM6ATMmsalXGvXAJ9jNK7cM9AO3JWL2g8ptvIHEPchcQCfT23338UU
 UhQU8vB5arkKl5jEWAk=
 =fayw
 -----END PGP SIGNATURE-----

Merge tag 'video-fixes-for-2019.04-rc4' of git://git.denx.de/u-boot-video

sunxi HDMI clock fix
This commit is contained in:
Tom Rini 2019-03-31 07:25:11 -04:00
commit 48cf0d8c6d

View file

@ -132,7 +132,7 @@ static int sunxi_dw_hdmi_wait_for_hpd(void)
return -1; 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 * const phy =
(struct sunxi_hdmi_phy *)(SUNXI_HDMI_BASE + HDMI_PHY_OFFS); (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) { switch (div) {
case 1: case 1:
writel(0x30dc5fc0, &phy->pll); writel(0x30dc5fc0, &phy->pll);
writel(0x800863C0, &phy->clk); writel(0x800863C0 | (phy_div - 1), &phy->clk);
mdelay(10); mdelay(10);
writel(0x00000001, &phy->unk3); writel(0x00000001, &phy->unk3);
setbits_le32(&phy->pll, BIT(25)); setbits_le32(&phy->pll, BIT(25));
@ -164,7 +164,7 @@ static void sunxi_dw_hdmi_phy_set(uint clock)
break; break;
case 2: case 2:
writel(0x39dc5040, &phy->pll); writel(0x39dc5040, &phy->pll);
writel(0x80084381, &phy->clk); writel(0x80084380 | (phy_div - 1), &phy->clk);
mdelay(10); mdelay(10);
writel(0x00000001, &phy->unk3); writel(0x00000001, &phy->unk3);
setbits_le32(&phy->pll, BIT(25)); setbits_le32(&phy->pll, BIT(25));
@ -178,7 +178,7 @@ static void sunxi_dw_hdmi_phy_set(uint clock)
break; break;
case 4: case 4:
writel(0x39dc5040, &phy->pll); writel(0x39dc5040, &phy->pll);
writel(0x80084343, &phy->clk); writel(0x80084340 | (phy_div - 1), &phy->clk);
mdelay(10); mdelay(10);
writel(0x00000001, &phy->unk3); writel(0x00000001, &phy->unk3);
setbits_le32(&phy->pll, BIT(25)); setbits_le32(&phy->pll, BIT(25));
@ -192,7 +192,7 @@ static void sunxi_dw_hdmi_phy_set(uint clock)
break; break;
case 11: case 11:
writel(0x39dc5040, &phy->pll); writel(0x39dc5040, &phy->pll);
writel(0x8008430a, &phy->clk); writel(0x80084300 | (phy_div - 1), &phy->clk);
mdelay(10); mdelay(10);
writel(0x00000001, &phy->unk3); writel(0x00000001, &phy->unk3);
setbits_le32(&phy->pll, BIT(25)); 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 value, n, m, div, diff;
int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF; int best_n = 0, best_m = 0, best_div = 0, best_diff = 0x0FFFFFFF;
div = sunxi_dw_hdmi_get_divider(clk_khz * 1000);
/* /*
* Find the lowest divider resulting in a matching clock. If there * Find the lowest divider resulting in a matching clock. If there
* is no match, pick the closest lower clock, as monitors tend to * is no match, pick the closest lower clock, as monitors tend to
* not sync to higher frequencies. * not sync to higher frequencies.
*/ */
for (m = 1; m <= 16; m++) { for (div = 1; div <= 16; div++) {
n = (m * div * clk_khz) / 24000; int target = clk_khz * div;
if ((n >= 1) && (n <= 128)) { if (target < 192000)
value = (24000 * n) / m / div; continue;
diff = clk_khz - value; if (target > 912000)
if (diff < best_diff) { continue;
best_diff = diff;
best_m = m; for (m = 1; m <= 16; m++) {
best_n = n; 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); clock_set_pll3_factors(best_m, best_n);
debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n", debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n",
clk_khz, (clock_get_pll3() / 1000) / div, clk_khz, (clock_get_pll3() / 1000) / best_div,
best_n, best_m, div); best_n, best_m, best_div);
} }
static void sunxi_dw_hdmi_lcdc_init(int mux, const struct display_timing *edid, 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 * const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE; (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; struct sunxi_lcdc_reg *lcdc;
if (mux == 0) { 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) static int sunxi_dw_hdmi_phy_cfg(struct dw_hdmi *hdmi, uint mpixelclock)
{ {
sunxi_dw_hdmi_pll_set(mpixelclock/1000); int phy_div;
sunxi_dw_hdmi_phy_set(mpixelclock);
sunxi_dw_hdmi_pll_set(mpixelclock / 1000, &phy_div);
sunxi_dw_hdmi_phy_set(mpixelclock, phy_div);
return 0; return 0;
} }