From d9786d23808fcbc5dd3bd3913b036d42dd48653f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 25 Dec 2014 13:58:06 +0100 Subject: [PATCH] sunxi: video: Add VGA output support Add support for VGA directly from the sunxi SoC / display engine. Signed-off-by: Hans de Goede Acked-by: Ian Campbell --- arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 2 + arch/arm/include/asm/arch-sunxi/display.h | 76 +++++++++++++++++++ board/sunxi/Kconfig | 9 ++- configs/A20-OLinuXino_MICRO_defconfig | 1 + configs/Cubietruck_defconfig | 1 + configs/Mele_A1000G_defconfig | 1 + configs/Mele_A1000_defconfig | 1 + configs/Mele_M3_defconfig | 1 + drivers/video/sunxi_display.c | 60 +++++++++++++-- 9 files changed, 143 insertions(+), 9 deletions(-) diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index dbc5e58cd5..64b5c389e5 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -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) diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h index 1d4e935017..5ce355d7b0 100644 --- a/arch/arm/include/asm/arch-sunxi/display.h +++ b/arch/arm/include/asm/arch-sunxi/display.h @@ -160,6 +160,52 @@ struct sunxi_hdmi_reg { #endif }; +/* + * 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. */ @@ -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 */ diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index 5959577749..4a6f49fd58 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -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 diff --git a/configs/A20-OLinuXino_MICRO_defconfig b/configs/A20-OLinuXino_MICRO_defconfig index 1b9668d46d..1c5a6f7a9f 100644 --- a/configs/A20-OLinuXino_MICRO_defconfig +++ b/configs/A20-OLinuXino_MICRO_defconfig @@ -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 diff --git a/configs/Cubietruck_defconfig b/configs/Cubietruck_defconfig index b1f9f936fa..bc4441082b 100644 --- a/configs/Cubietruck_defconfig +++ b/configs/Cubietruck_defconfig @@ -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 diff --git a/configs/Mele_A1000G_defconfig b/configs/Mele_A1000G_defconfig index 2f4bf72c93..9cb3285a71 100644 --- a/configs/Mele_A1000G_defconfig +++ b/configs/Mele_A1000G_defconfig @@ -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 diff --git a/configs/Mele_A1000_defconfig b/configs/Mele_A1000_defconfig index e2912b0afd..97d94542d3 100644 --- a/configs/Mele_A1000_defconfig +++ b/configs/Mele_A1000_defconfig @@ -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 diff --git a/configs/Mele_M3_defconfig b/configs/Mele_M3_defconfig index fe9ba11c39..141d565cf8 100644 --- a/configs/Mele_M3_defconfig +++ b/configs/Mele_M3_defconfig @@ -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 diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 5d5efd63cf..536e1cac4d 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -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; } -- 2.39.5