[PATCH RFC 9/9] sunxi: video: support HDMI on H6/H616

John Watts contact at jookia.org
Sat Apr 20 02:02:42 CEST 2024


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 at 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);

-- 
2.44.0



More information about the U-Boot mailing list