This commit adds support for TV (composite) output.

Because there is no mechanism to select TV standard, PAL is hardcoded.

Signed-off-by: Jernej Skrabec <[email protected]>
---

 arch/arm/include/asm/arch-sunxi/tve.h |  17 +++-
 drivers/video/sunxi/Makefile          |   2 +-
 drivers/video/sunxi/sunxi_tve.c       | 156 ++++++++++++++++++++++++++++++++++
 drivers/video/sunxi/tve_common.c      |   6 +-
 4 files changed, 176 insertions(+), 5 deletions(-)
 create mode 100644 drivers/video/sunxi/sunxi_tve.c

diff --git a/arch/arm/include/asm/arch-sunxi/tve.h 
b/arch/arm/include/asm/arch-sunxi/tve.h
index 41a14a68e4..ff34bbbc12 100644
--- a/arch/arm/include/asm/arch-sunxi/tve.h
+++ b/arch/arm/include/asm/arch-sunxi/tve.h
@@ -45,7 +45,9 @@ struct sunxi_tve_reg {
        u32 csc_reg1;                   /* 0x044 */
        u32 csc_reg2;                   /* 0x048 */
        u32 csc_reg3;                   /* 0x04c */
-       u8 res1[0xb0];                  /* 0x050 */
+       u8 res1[0xa8];                  /* 0x050 */
+       u32 auto_detect_cfg0;           /* 0x0f8 */
+       u32 auto_detect_cfg1;           /* 0x0fc */
        u32 color_burst;                /* 0x100 */
        u32 vsync_num;                  /* 0x104 */
        u32 notch_freq;                 /* 0x108 */
@@ -62,6 +64,10 @@ struct sunxi_tve_reg {
        u32 slave_para;                 /* 0x134 */
        u32 cfg1;                       /* 0x138 */
        u32 cfg2;                       /* 0x13c */
+       u8 res2[0x1c4];                 /* 0x140 */
+       u32 calibration;                /* 0x304 */
+       u8 res3[0x4];                   /* 0x308 */
+       u32 unknown3;                   /* 0x30c */
 };
 
 /*
@@ -79,12 +85,14 @@ struct sunxi_tve_reg {
 #define SUNXI_TVE_CFG0_PAL                     0x07030001
 #define SUNXI_TVE_CFG0_NTSC                    0x07030000
 #define SUNXI_TVE_DAC_CFG0_VGA                 0x403e1ac7
-#ifdef CONFIG_MACH_SUN5I
+#if defined(CONFIG_MACH_SUN5I) || defined(CONFIG_MACH_SUNXI_H3_H5)
 #define SUNXI_TVE_DAC_CFG0_COMPOSITE           0x433f0009
 #else
 #define SUNXI_TVE_DAC_CFG0_COMPOSITE           0x403f0008
 #endif
+#define SUNXI_TVE_DAC_CFG0_DETECTION           0x433f0289
 #define SUNXI_TVE_FILTER_COMPOSITE             0x00000120
+#define SUNXI_TVE_CHROMA_FREQ_PAL              0x2a098acb
 #define SUNXI_TVE_CHROMA_FREQ_PAL_M            0x21e6efe3
 #define SUNXI_TVE_CHROMA_FREQ_PAL_NC           0x21f69446
 #define SUNXI_TVE_PORCH_NUM_PAL                        0x008a0018
@@ -105,6 +113,8 @@ struct sunxi_tve_reg {
 #define SUNXI_TVE_AUTO_DETECT_STATUS_SHORT_GND 3
 #define SUNXI_TVE_AUTO_DETECT_DEBOUNCE_SHIFT(d)        ((d) * 8)
 #define SUNXI_TVE_AUTO_DETECT_DEBOUNCE_MASK(d) (0xf << ((d) * 8))
+#define SUNXI_TVE_AUTO_DETECT_CFG0             0x00000280
+#define SUNXI_TVE_AUTO_DETECT_CFG1             0x028F00FF
 #define SUNXI_TVE_CSC_REG0_ENABLE              (1 << 31)
 #define SUNXI_TVE_CSC_REG0                     0x08440832
 #define SUNXI_TVE_CSC_REG1                     0x3b6dace1
@@ -124,6 +134,9 @@ struct sunxi_tve_reg {
 #define SUNXI_TVE_RESYNC_NUM_PAL               0x800d000c
 #define SUNXI_TVE_RESYNC_NUM_NTSC              0x000e000c
 #define SUNXI_TVE_SLAVE_PARA_COMPOSITE         0x00000000
+#define SUNXI_TVE_CALIBRATION_H3               0x02000c00
+#define SUNXI_TVE_CALIBRATION_H5               0x02850000
+#define SUNXI_TVE_UNKNOWN3_H5                  0x00101110
 
 void tvencoder_mode_set(struct sunxi_tve_reg * const tve, enum tve_mode mode);
 void tvencoder_enable(struct sunxi_tve_reg * const tve);
diff --git a/drivers/video/sunxi/Makefile b/drivers/video/sunxi/Makefile
index 0d64c2021f..e63c1d65bc 100644
--- a/drivers/video/sunxi/Makefile
+++ b/drivers/video/sunxi/Makefile
@@ -6,4 +6,4 @@
 #
 
 obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o lcdc.o tve_common.o 
../videomodes.o
-obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o lcdc.o ../dw_hdmi.o
+obj-$(CONFIG_VIDEO_DE2) += sunxi_de2.o sunxi_dw_hdmi.o sunxi_tve.o lcdc.o 
tve_common.o ../dw_hdmi.o
diff --git a/drivers/video/sunxi/sunxi_tve.c b/drivers/video/sunxi/sunxi_tve.c
new file mode 100644
index 0000000000..95f54bbaf7
--- /dev/null
+++ b/drivers/video/sunxi/sunxi_tve.c
@@ -0,0 +1,156 @@
+/*
+ * Allwinner TVE driver
+ *
+ * (C) Copyright 2017 Jernej Skrabec <[email protected]>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <display.h>
+#include <dm.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/lcdc.h>
+#include <asm/arch/tve.h>
+
+static int sunxi_tve_get_plug_in_status(void)
+{
+       struct sunxi_tve_reg * const tve =
+               (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
+       u32 status;
+
+       status = readl(&tve->auto_detect_status) &
+               SUNXI_TVE_AUTO_DETECT_STATUS_MASK(0);
+
+       return status == SUNXI_TVE_AUTO_DETECT_STATUS_CONNECTED;
+}
+
+static int sunxi_tve_wait_for_hpd(void)
+{
+       struct sunxi_tve_reg * const tve =
+               (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
+       ulong start;
+
+       /* enable auto detection */
+       writel(SUNXI_TVE_DAC_CFG0_DETECTION, &tve->dac_cfg0);
+       writel(SUNXI_TVE_AUTO_DETECT_CFG0, &tve->auto_detect_cfg0);
+       writel(SUNXI_TVE_AUTO_DETECT_CFG1, &tve->auto_detect_cfg1);
+       writel(9 << SUNXI_TVE_AUTO_DETECT_DEBOUNCE_SHIFT(0),
+              &tve->auto_detect_debounce);
+       writel(SUNXI_TVE_AUTO_DETECT_EN_DET_EN(0), &tve->auto_detect_en);
+
+       start = get_timer(0);
+       do {
+               if (sunxi_tve_get_plug_in_status())
+                       return 0;
+               udelay(100);
+       } while (get_timer(start) < 300);
+
+       return -1;
+}
+
+static void sunxi_tve_lcdc_init(const struct display_timing *edid, int bpp)
+{
+       struct sunxi_ccm_reg * const ccm =
+               (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+       struct sunxi_lcdc_reg * const lcdc =
+               (struct sunxi_lcdc_reg *)SUNXI_LCD1_BASE;
+
+       /* Reset off */
+       setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_LCD1);
+
+       /* Clock on */
+       setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD1);
+
+       lcdc_init(lcdc);
+       lcdc_tcon1_mode_set(lcdc, edid, false, true);
+       lcdc_enable(lcdc, bpp);
+}
+
+static int sunxi_tve_read_timing(struct udevice *dev,
+                                struct display_timing *timing)
+{
+       /* PAL resolution */
+       timing->pixelclock.typ = 27000000;
+
+       timing->hactive.typ = 720;
+       timing->hfront_porch.typ = 5;
+       timing->hback_porch.typ = 137;
+       timing->hsync_len.typ = 2;
+
+       timing->vactive.typ = 576;
+       timing->vfront_porch.typ = 27;
+       timing->vback_porch.typ = 20;
+       timing->vsync_len.typ = 2;
+
+       timing->flags = DISPLAY_FLAGS_INTERLACED;
+
+       return 0;
+}
+
+static int sunxi_tve_enable(struct udevice *dev, int panel_bpp,
+                           const struct display_timing *edid)
+{
+       struct sunxi_tve_reg * const tve =
+               (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
+
+       sunxi_tve_lcdc_init(edid, panel_bpp);
+
+       tvencoder_mode_set(tve, tve_mode_composite_pal);
+       tvencoder_enable(tve);
+
+       return 0;
+}
+
+static int sunxi_tve_probe(struct udevice *dev)
+{
+       struct sunxi_ccm_reg * const ccm =
+               (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+       struct sunxi_tve_reg * const tve =
+               (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
+       int ret;
+
+       /* make sure that clock is active */
+       clock_set_pll10(432000000);
+
+       /* Reset off */
+       setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_TVE);
+
+       /* Clock on */
+       setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE);
+       writel(CCM_TVE_CTRL_GATE | CCM_TVE_CTRL_M(2), &ccm->tve_clk_cfg);
+
+#ifdef CONFIG_MACH_SUN50I_H5
+       writel(SUNXI_TVE_CALIBRATION_H5, &tve->calibration);
+       writel(SUNXI_TVE_UNKNOWN3_H5, &tve->unknown3);
+#else
+       writel(SUNXI_TVE_CALIBRATION_H3, &tve->calibration);
+#endif
+
+       ret = sunxi_tve_wait_for_hpd();
+       if (ret < 0) {
+               debug("tve can not get hpd signal\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static const struct dm_display_ops sunxi_tve_ops = {
+       .read_timing = sunxi_tve_read_timing,
+       .enable = sunxi_tve_enable,
+};
+
+U_BOOT_DRIVER(sunxi_tve) = {
+       .name   = "sunxi_tve",
+       .id     = UCLASS_DISPLAY,
+       .ops    = &sunxi_tve_ops,
+       .probe  = sunxi_tve_probe,
+};
+
+#ifdef CONFIG_MACH_SUNXI_H3_H5
+U_BOOT_DEVICE(sunxi_tve) = {
+       .name = "sunxi_tve"
+};
+#endif
diff --git a/drivers/video/sunxi/tve_common.c b/drivers/video/sunxi/tve_common.c
index adea78a69a..ef99c111e3 100644
--- a/drivers/video/sunxi/tve_common.c
+++ b/drivers/video/sunxi/tve_common.c
@@ -25,8 +25,6 @@ void tvencoder_mode_set(struct sunxi_tve_reg * const tve, 
enum tve_mode mode)
                writel(SUNXI_TVE_UNKNOWN1_VGA, &tve->unknown1);
                break;
        case tve_mode_composite_pal_nc:
-               writel(SUNXI_TVE_CHROMA_FREQ_PAL_NC, &tve->chroma_freq);
-               /* Fall through */
        case tve_mode_composite_pal:
                writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
                       SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
@@ -35,6 +33,10 @@ void tvencoder_mode_set(struct sunxi_tve_reg * const tve, 
enum tve_mode mode)
                writel(SUNXI_TVE_CFG0_PAL, &tve->cfg0);
                writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0);
                writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter);
+               if (mode == tve_mode_composite_pal)
+                       writel(SUNXI_TVE_CHROMA_FREQ_PAL, &tve->chroma_freq);
+               else
+                       writel(SUNXI_TVE_CHROMA_FREQ_PAL_NC, &tve->chroma_freq);
                writel(SUNXI_TVE_PORCH_NUM_PAL, &tve->porch_num);
                writel(SUNXI_TVE_LINE_NUM_PAL, &tve->line_num);
                writel(SUNXI_TVE_BLANK_BLACK_LEVEL_PAL,
-- 
2.13.0

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to