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.
