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 <cont...@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

Reply via email to