[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