sunxi: video: Add VGA output support
authorHans de Goede <hdegoede@redhat.com>
Thu, 25 Dec 2014 12:58:06 +0000 (13:58 +0100)
committerHans de Goede <hdegoede@redhat.com>
Wed, 14 Jan 2015 13:56:39 +0000 (14:56 +0100)
Add support for VGA directly from the sunxi SoC / display engine.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Ian Campbell <ijc@hellion.org.uk>
arch/arm/include/asm/arch-sunxi/clock_sun4i.h
arch/arm/include/asm/arch-sunxi/display.h
board/sunxi/Kconfig
configs/A20-OLinuXino_MICRO_defconfig
configs/Cubietruck_defconfig
configs/Mele_A1000G_defconfig
configs/Mele_A1000_defconfig
configs/Mele_M3_defconfig
drivers/video/sunxi_display.c

index dbc5e58..64b5c38 100644 (file)
@@ -190,6 +190,8 @@ struct sunxi_ccm_reg {
 #define AHB_GATE_OFFSET_HDMI           11
 #define AHB_GATE_OFFSET_LCD1           5
 #define AHB_GATE_OFFSET_LCD0           4
+#define AHB_GATE_OFFSET_TVE1           3
+#define AHB_GATE_OFFSET_TVE0           2
 
 #define CCM_AHB_GATE_GPS (0x1 << 26)
 #define CCM_AHB_GATE_SDRAM (0x1 << 14)
index 1d4e935..5ce355d 100644 (file)
@@ -161,6 +161,52 @@ struct sunxi_hdmi_reg {
 };
 
 /*
+ * This is based on the A10s User Manual, and the A10s only supports
+ * composite video and not vga like the A10 / A20 does, still other
+ * than the removed vga out capability the tvencoder seems to be the same.
+ * "unknown#" registers are registers which are used in the A10 kernel code,
+ * but not documented in the A10s User Manual.
+ */
+struct sunxi_tve_reg {
+       u32 gctrl;                      /* 0x000 */
+       u32 cfg0;                       /* 0x004 */
+       u32 dac_cfg0;                   /* 0x008 */
+       u32 filter;                     /* 0x00c */
+       u32 chroma_freq;                /* 0x010 */
+       u32 porch_num;                  /* 0x014 */
+       u32 unknown0;                   /* 0x018 */
+       u32 line_num;                   /* 0x01c */
+       u32 blank_black_level;          /* 0x020 */
+       u32 unknown1;                   /* 0x024, seems to be 1 byte per dac */
+       u8 res0[0x08];                  /* 0x028 */
+       u32 auto_detect_en;             /* 0x030 */
+       u32 auto_detect_int_status;     /* 0x034 */
+       u32 auto_detect_status;         /* 0x038 */
+       u32 auto_detect_debounce;       /* 0x03c */
+       u32 csc_reg0;                   /* 0x040 */
+       u32 csc_reg1;                   /* 0x044 */
+       u32 csc_reg2;                   /* 0x048 */
+       u32 csc_reg3;                   /* 0x04c */
+       u8 res1[0xb0];                  /* 0x050 */
+       u32 color_burst;                /* 0x100 */
+       u32 vsync_num;                  /* 0x104 */
+       u32 notch_freq;                 /* 0x108 */
+       u32 cbr_level;                  /* 0x10c */
+       u32 burst_phase;                /* 0x110 */
+       u32 burst_width;                /* 0x114 */
+       u8 res2[0x04];                  /* 0x118 */
+       u32 sync_vbi_level;             /* 0x11c */
+       u32 white_level;                /* 0x120 */
+       u32 active_num;                 /* 0x124 */
+       u32 chroma_bw_gain;             /* 0x128 */
+       u32 notch_width;                /* 0x12c */
+       u32 resync_num;                 /* 0x130 */
+       u32 slave_para;                 /* 0x134 */
+       u32 cfg1;                       /* 0x138 */
+       u32 cfg2;                       /* 0x13c */
+};
+
+/*
  * DE-BE register constants.
  */
 #define SUNXI_DE_BE_WIDTH(x)                   (((x) - 1) << 0)
@@ -299,6 +345,36 @@ struct sunxi_hdmi_reg {
 #define SUNXI_HMDI_DDC_LINE_CTRL_SCL_ENABLE    (1 << 8)
 #define SUNXI_HMDI_DDC_LINE_CTRL_SDA_ENABLE    (1 << 9)
 
+/*
+ * TVE register constants.
+ */
+#define SUNXI_TVE_GCTRL_ENABLE                 (1 << 0)
+/*
+ * Select input 0 to disable dac, 1 - 4 to feed dac from tve0, 5 - 8 to feed
+ * dac from tve1. When using tve1 the mux value must be written to both tve0's
+ * and tve1's gctrl reg.
+ */
+#define SUNXI_TVE_GCTRL_DAC_INPUT_MASK(dac)    (0xf << (((dac) + 1) * 4))
+#define SUNXI_TVE_GCTRL_DAC_INPUT(dac, sel)    ((sel) << (((dac) + 1) * 4))
+#define SUNXI_TVE_GCTRL_CFG0_VGA               0x20000000
+#define SUNXI_TVE_GCTRL_DAC_CFG0_VGA           0x403e1ac7
+#define SUNXI_TVE_GCTRL_UNKNOWN1_VGA           0x00000000
+#define SUNXI_TVE_AUTO_DETECT_EN_DET_EN(dac)   (1 << ((dac) + 0))
+#define SUNXI_TVE_AUTO_DETECT_EN_INT_EN(dac)   (1 << ((dac) + 16))
+#define SUNXI_TVE_AUTO_DETECT_INT_STATUS(dac)  (1 << ((dac) + 0))
+#define SUNXI_TVE_AUTO_DETECT_STATUS_SHIFT(dac)        ((dac) * 8)
+#define SUNXI_TVE_AUTO_DETECT_STATUS_MASK(dac) (3 << ((dac) * 8))
+#define SUNXI_TVE_AUTO_DETECT_STATUS_NONE      0
+#define SUNXI_TVE_AUTO_DETECT_STATUS_CONNECTED 1
+#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_CSC_REG0_ENABLE              (1 << 31)
+#define SUNXI_TVE_CSC_REG0                     0x08440832
+#define SUNXI_TVE_CSC_REG1                     0x3b6dace1
+#define SUNXI_TVE_CSC_REG2                     0x0e1d13dc
+#define SUNXI_TVE_CSC_REG3                     0x00108080
+
 int sunxi_simplefb_setup(void *blob);
 
 #endif /* _SUNXI_DISPLAY_H */
index 5959577..4a6f49f 100644 (file)
@@ -294,9 +294,16 @@ config VIDEO_HDMI
        ---help---
        Say Y here to add support for outputting video over HDMI.
 
+config VIDEO_VGA
+       boolean "VGA output support"
+       depends on VIDEO && (MACH_SUN4I || MACH_SUN7I)
+       default n
+       ---help---
+       Say Y here to add support for outputting video over VGA.
+
 config VIDEO_VGA_VIA_LCD
        boolean "VGA via LCD controller support"
-       depends on VIDEO
+       depends on VIDEO && MACH_SUN5I
        default n
        ---help---
        Say Y here to add support for external DACs connected to the parallel
index 1b9668d..1c5a6f7 100644 (file)
@@ -2,6 +2,7 @@ CONFIG_SPL=y
 CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_GMAC,AHCI,SATAPWR=SUNXI_GPB(8),USB_EHCI"
 CONFIG_FDTFILE="sun7i-a20-olinuxino-micro.dtb"
 CONFIG_MMC_SUNXI_SLOT_EXTRA=3
+CONFIG_VIDEO_VGA=y
 +S:CONFIG_MMC0_CD_PIN="PH1"
 +S:CONFIG_MMC3_CD_PIN="PH11"
 +S:CONFIG_ARM=y
index b1f9f93..bc44410 100644 (file)
@@ -1,6 +1,7 @@
 CONFIG_SPL=y
 CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_GMAC,RGMII,AHCI,SATAPWR=SUNXI_GPH(12),USB_EHCI"
 CONFIG_FDTFILE="sun7i-a20-cubietruck.dtb"
+CONFIG_VIDEO_VGA=y
 +S:CONFIG_ARM=y
 +S:CONFIG_ARCH_SUNXI=y
 +S:CONFIG_MACH_SUN7I=y
index 2f4bf72..9cb3285 100644 (file)
@@ -1,6 +1,7 @@
 CONFIG_SPL=y
 CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_EMAC,MACPWR=SUNXI_GPH(15),AHCI,USB_EHCI"
 CONFIG_FDTFILE="sun4i-a10-a1000.dtb"
+CONFIG_VIDEO_VGA=y
 +S:CONFIG_ARM=y
 +S:CONFIG_ARCH_SUNXI=y
 +S:CONFIG_MACH_SUN4I=y
index e2912b0..97d9454 100644 (file)
@@ -1,6 +1,7 @@
 CONFIG_SPL=y
 CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_EMAC,MACPWR=SUNXI_GPH(15),AHCI,USB_EHCI"
 CONFIG_FDTFILE="sun4i-a10-a1000.dtb"
+CONFIG_VIDEO_VGA=y
 +S:CONFIG_ARM=y
 +S:CONFIG_ARCH_SUNXI=y
 +S:CONFIG_MACH_SUN4I=y
index fe9ba11..141d565 100644 (file)
@@ -1,6 +1,7 @@
 CONFIG_SPL=y
 CONFIG_SYS_EXTRA_OPTIONS="AXP209_POWER,SUNXI_GMAC,USB_EHCI"
 CONFIG_FDTFILE="sun7i-a20-m3.dtb"
+CONFIG_VIDEO_VGA=y
 +S:CONFIG_MMC_SUNXI_SLOT_EXTRA=2
 +S:CONFIG_MMC0_CD_PIN="PH1"
 +S:CONFIG_ARM=y
index 5d5efd6..536e1ca 100644 (file)
@@ -569,8 +569,7 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
        writel(0, &lcdc->tcon0_io_tristate);
 }
 
-#ifdef CONFIG_VIDEO_HDMI
-
+#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA
 static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
                                      int *clk_div, int *clk_double,
                                      bool use_portd_hvsync)
@@ -624,6 +623,9 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
        }
        sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double);
 }
+#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA */
+
+#ifdef CONFIG_VIDEO_HDMI
 
 static void sunxi_hdmi_setup_info_frames(const struct ctfb_res_modes *mode)
 {
@@ -735,6 +737,37 @@ static void sunxi_hdmi_enable(void)
 
 #endif /* CONFIG_VIDEO_HDMI */
 
+#ifdef CONFIG_VIDEO_VGA
+
+static void sunxi_vga_mode_set(void)
+{
+       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;
+
+       /* Clock on */
+       setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0);
+
+       /* Set TVE in VGA mode */
+       writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) |
+              SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) |
+              SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl);
+       writel(SUNXI_TVE_GCTRL_CFG0_VGA, &tve->cfg0);
+       writel(SUNXI_TVE_GCTRL_DAC_CFG0_VGA, &tve->dac_cfg0);
+       writel(SUNXI_TVE_GCTRL_UNKNOWN1_VGA, &tve->unknown1);
+}
+
+static void sunxi_vga_enable(void)
+{
+       struct sunxi_tve_reg * const tve =
+               (struct sunxi_tve_reg *)SUNXI_TVE0_BASE;
+
+       setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE);
+}
+
+#endif /* CONFIG_VIDEO_VGA */
+
 static void sunxi_drc_init(void)
 {
 #if defined CONFIG_MACH_SUN6I || defined CONFIG_MACH_SUN8I
@@ -757,13 +790,14 @@ static void sunxi_engines_init(void)
 static void sunxi_mode_set(const struct ctfb_res_modes *mode,
                           unsigned int address)
 {
+       int __maybe_unused clk_div, clk_double;
+
        switch (sunxi_display.monitor) {
        case sunxi_monitor_none:
                break;
        case sunxi_monitor_dvi:
-       case sunxi_monitor_hdmi: {
+       case sunxi_monitor_hdmi:
 #ifdef CONFIG_VIDEO_HDMI
-               int clk_div, clk_double;
                sunxi_composer_mode_set(mode, address);
                sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
                sunxi_hdmi_mode_set(mode, clk_div, clk_double);
@@ -771,7 +805,6 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
                sunxi_lcdc_enable();
                sunxi_hdmi_enable();
 #endif
-               }
                break;
        case sunxi_monitor_lcd:
                sunxi_lcdc_panel_enable();
@@ -782,7 +815,14 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode,
                sunxi_lcdc_backlight_enable();
                break;
        case sunxi_monitor_vga:
-#ifdef CONFIG_VIDEO_VGA_VIA_LCD
+#ifdef CONFIG_VIDEO_VGA
+               sunxi_composer_mode_set(mode, address);
+               sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
+               sunxi_vga_mode_set();
+               sunxi_composer_enable();
+               sunxi_lcdc_enable();
+               sunxi_vga_enable();
+#elif defined CONFIG_VIDEO_VGA_VIA_LCD
                sunxi_composer_mode_set(mode, address);
                sunxi_lcdc_tcon0_mode_set(mode);
                sunxi_composer_enable();
@@ -862,7 +902,7 @@ void *video_hw_init(void)
                        if (lcd_mode[0]) {
                                sunxi_display.monitor = sunxi_monitor_lcd;
                        } else {
-#ifdef CONFIG_VIDEO_VGA_VIA_LCD
+#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
                                sunxi_display.monitor = sunxi_monitor_vga;
 #else
                                sunxi_display.monitor = sunxi_monitor_none;
@@ -894,7 +934,7 @@ void *video_hw_init(void)
                sunxi_display.monitor = sunxi_monitor_none;
                return NULL;
        case sunxi_monitor_vga:
-#ifdef CONFIG_VIDEO_VGA_VIA_LCD
+#if defined CONFIG_VIDEO_VGA_VIA_LCD || defined CONFIG_VIDEO_VGA
                sunxi_display.depth = 18;
                break;
 #else
@@ -950,7 +990,11 @@ int sunxi_simplefb_setup(void *blob)
                pipeline = "de_be0-lcd0";
                break;
        case sunxi_monitor_vga:
+#ifdef CONFIG_VIDEO_VGA
+               pipeline = "de_be0-lcd0-tve0";
+#elif defined CONFIG_VIDEO_VGA_VIA_LCD
                pipeline = "de_be0-lcd0";
+#endif
                break;
        }