
The H6 and H616 support outputting HDMI through the Display Engine. Set up the clocks and resets appropriately for the HDMI controller.
This turns out to be a little tricky as the HDMI clock requires a different parent on the H6 compared to the H616. So we have to end up choosing VIDEO1 on the H616 and VIDEO0 elsewhere.
Signed-off-by: John Watts contact@jookia.org --- arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h | 9 +++ arch/arm/mach-sunxi/clock_sun50i_h6.c | 10 ++++ drivers/video/sunxi/sunxi_dw_hdmi.c | 70 ++++++++++++++++++++--- 3 files changed, 80 insertions(+), 9 deletions(-)
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h index dfe8d9315f..35bd3dd2d8 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun50i_h6.h @@ -376,13 +376,21 @@ struct sunxi_ccm_reg { /* TCON0 clock bit field */ #define CCM_TCON0_CTRL_ENABLE (0x1 << 31) #define CCM_TCON0_CTRL_VIDEO0_4X (0x1 << 24) +#define CCM_TCON0_CTRL_VIDEO1_4X (0x3 << 24) #define CCM_TCON0_CTRL_M(m) ((((m) - 1) & 0xf) << 0)
/* TCON1 clock bit field */ #define CCM_TCON1_CTRL_ENABLE (0x1 << 31) #define CCM_TCON1_CTRL_VIDEO0_4X (0x1 << 24) +#define CCM_TCON1_CTRL_VIDEO1_4X (0x3 << 24) #define CCM_TCON1_CTRL_M(m) ((((m) - 1) & 0xf) << 0)
+/* HDMI clock bit field */ +#define CCM_HDMI_CTRL_ENABLE (0x1 << 31) +#define CCM_HDMI_CTRL_VIDEO1_4X_H6 (0x2 << 24) +#define CCM_HDMI_CTRL_VIDEO0_4X_H616 (0x1 << 24) +#define CCM_HDMI_CTRL_M(m) ((((m) - 1) & 0xf) << 0) + /* CCM bits common to all Display Engine 2.0 clock ctrl regs */ #define CCM_DE2_CTRL_M(n) ((((n) - 1) & 0xf) << 0) #define CCM_DE2_CTRL_PLL_MASK (3 << 24) @@ -399,6 +407,7 @@ void clock_set_pll3(unsigned int hz); void clock_set_video1(unsigned int hz); void clock_set_pll10(unsigned int hz); unsigned int clock_get_pll3(void); +unsigned int clock_get_video1(void); #endif #endif
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c index 11e303f801..23b7c13e28 100644 --- a/arch/arm/mach-sunxi/clock_sun50i_h6.c +++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c @@ -230,4 +230,14 @@ unsigned int clock_get_pll3(void) return 12000 * n * 1000; }
+unsigned int clock_get_video1(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + u32 rval = readl(&ccm->pll_video1_cfg); + int n = ((rval & CCM_VIDEO1_CTRL_N_MASK) >> CCM_VIDEO1_CTRL_N_SHIFT) + 1; + + return 12000 * n * 1000; +} + #endif diff --git a/drivers/video/sunxi/sunxi_dw_hdmi.c b/drivers/video/sunxi/sunxi_dw_hdmi.c index 34a6b8bab7..475d61a888 100644 --- a/drivers/video/sunxi/sunxi_dw_hdmi.c +++ b/drivers/video/sunxi/sunxi_dw_hdmi.c @@ -198,6 +198,12 @@ static void sunxi_dw_hdmi_pll_set(uint clk_khz, int *phy_div) { int value, n, m, div, diff; int best_n = 0, best_m = 0, best_div = 0, best_diff = 0x0FFFFFFF; + int step = 24000, max_m = 16, pll_value = 0; + + if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) { + step = 12000; + max_m = 1; + }
/* * Find the lowest divider resulting in a matching clock. If there @@ -212,11 +218,11 @@ static void sunxi_dw_hdmi_pll_set(uint clk_khz, int *phy_div) if (target > 912000) continue;
- for (m = 1; m <= 16; m++) { - n = (m * target) / 24000; + for (m = 1; m <= max_m; m++) { + n = (m * target) / step;
if (n >= 1 && n <= 128) { - value = (24000 * n) / m / div; + value = (step * n) / m / div; diff = clk_khz - value; if (diff < best_diff) { best_diff = diff; @@ -231,13 +237,20 @@ static void sunxi_dw_hdmi_pll_set(uint clk_khz, int *phy_div) *phy_div = best_div;
#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2) - panic("setting HDMI pll not implemented"); + if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) { + clock_set_video1(step * best_n); + pll_value = clock_get_video1(); + } else { + clock_set_pll3(step * best_n); + pll_value = clock_get_pll3(); + } #else clock_set_pll3_factors(best_m, best_n); + pll_value = clock_get_pll3(); #endif
debug("dotclock: %dkHz = %dkHz: (24MHz * %d) / %d / %d\n", - clk_khz, (clock_get_pll3() / 1000) / best_div, + clk_khz, (pll_value / 1000) / best_div, best_n, best_m, best_div); }
@@ -246,12 +259,34 @@ 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 = DIV_ROUND_UP(clock_get_pll3(), edid->pixelclock.typ); + int div, pll_value; struct sunxi_lcdc_reg *lcdc;
#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2) - panic("initializing HDMI lcdc not implemented"); + int tcon1_src; + + if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) { + tcon1_src = CCM_TCON1_CTRL_VIDEO1_4X; + pll_value = clock_get_video1(); + } else { + tcon1_src = CCM_TCON1_CTRL_VIDEO0_4X; + pll_value = clock_get_pll3(); + } + + div = DIV_ROUND_UP(pll_value, edid->pixelclock.typ); + + if (mux == 0) { + writel(tcon1_src | CCM_TCON1_CTRL_ENABLE | CCM_TCON1_CTRL_M(div), + &ccm->tcon_tv0_clk_cfg); + setbits_le32(&ccm->tcon_tv_gate_reset, BIT(RESET_SHIFT)); + setbits_le32(&ccm->tcon_tv_gate_reset, BIT(GATE_SHIFT)); + } else { + /* TODO: H616 supports a second TV encoder */ + panic("using HDMI lcdc mux 1 is not implemented"); + } #else + div = DIV_ROUND_UP(pll_value, edid->pixelclock.typ); + if (mux == 0) { lcdc = (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
@@ -300,7 +335,12 @@ static int sunxi_dw_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size) static bool sunxi_dw_hdmi_mode_valid(struct udevice *dev, const struct display_timing *timing) { - return timing->pixelclock.typ <= 297000000; + int max_clock = 297000000; + + if(IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) + max_clock = 594000; + + return timing->pixelclock.typ <= max_clock; }
static int sunxi_dw_hdmi_enable(struct udevice *dev, int panel_bpp, @@ -348,7 +388,19 @@ static int sunxi_dw_hdmi_probe(struct udevice *dev) regulator_set_enable(priv->hvcc, true);
#if IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2) - panic("initializing HDMI not implemented"); + int hdmi_src = CCM_HDMI_CTRL_VIDEO0_4X_H616; + + /* Set HDMI PLL to 297 MHz */ + if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) { + hdmi_src = CCM_HDMI_CTRL_VIDEO1_4X_H6; + clock_set_video1(297000000); + } else { + clock_set_pll3(297000000); + } + + writel(hdmi_src | CCM_HDMI_CTRL_ENABLE, &ccm->hdmi_clk_cfg); + setbits_le32(&ccm->hdmi_gate_reset, BIT(RESET_SHIFT)); + setbits_le32(&ccm->hdmi_gate_reset, BIT(GATE_SHIFT)); #else /* Set pll3 to 297 MHz */ clock_set_pll3(297000000);