diff mbox

[U-Boot,3/3] sunxi: video: Add H3/H5 TV out driver

Message ID 20170510164630.22775-4-jernej.skrabec@siol.net
State Superseded
Delegated to: Anatolij Gustschin
Headers show

Commit Message

Jernej Škrabec May 10, 2017, 4:46 p.m. UTC
This commit adds support for TV (composite) output.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---

 arch/arm/include/asm/arch-sunxi/cpu_sun4i.h |  10 ++
 arch/arm/include/asm/arch-sunxi/display2.h  |  17 +++
 arch/arm/include/asm/arch-sunxi/tve.h       |  17 ++-
 drivers/video/sunxi/Makefile                |   2 +-
 drivers/video/sunxi/sunxi_de2.c             |  60 ++++++++---
 drivers/video/sunxi/sunxi_tve.c             | 156 ++++++++++++++++++++++++++++
 drivers/video/sunxi/tve.c                   |   6 +-
 7 files changed, 251 insertions(+), 17 deletions(-)
 create mode 100644 drivers/video/sunxi/sunxi_tve.c

Comments

Maxime Ripard May 12, 2017, 4:06 p.m. UTC | #1
Hi Jernej,

The patch content looks fine, but there's a few things that would need
to be addressed.

On Wed, May 10, 2017 at 06:46:30PM +0200, Jernej Skrabec wrote:
> This commit adds support for TV (composite) output.
> 
> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> ---
> 
>  arch/arm/include/asm/arch-sunxi/cpu_sun4i.h |  10 ++
>  arch/arm/include/asm/arch-sunxi/display2.h  |  17 +++
>  arch/arm/include/asm/arch-sunxi/tve.h       |  17 ++-
>  drivers/video/sunxi/Makefile                |   2 +-
>  drivers/video/sunxi/sunxi_de2.c             |  60 ++++++++---
>  drivers/video/sunxi/sunxi_tve.c             | 156 ++++++++++++++++++++++++++++
>  drivers/video/sunxi/tve.c                   |   6 +-

The difference between sunxi_tve and tve is not really obvious. What
about calling sunxi_tve tve-uclass, or something like that?

>  7 files changed, 251 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/video/sunxi/sunxi_tve.c
> 
> diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
> index 6aa5e91ada..2419062d45 100644
> --- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
> +++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
> @@ -34,7 +34,9 @@
>  #define SUNXI_MS_BASE			0x01c07000
>  #define SUNXI_TVD_BASE			0x01c08000
>  #define SUNXI_CSI0_BASE			0x01c09000
> +#ifndef CONFIG_MACH_SUNXI_H3_H5
>  #define SUNXI_TVE0_BASE			0x01c0a000
> +#endif

This should be part of a seperate patch.

>  #define SUNXI_EMAC_BASE			0x01c0b000
>  #define SUNXI_LCD0_BASE			0x01c0C000
>  #define SUNXI_LCD1_BASE			0x01c0d000
> @@ -161,10 +163,18 @@ defined(CONFIG_MACH_SUN50I)
>  /* module sram */
>  #define SUNXI_SRAM_C_BASE		0x01d00000
>  
> +#ifndef CONFIG_MACH_SUN8I_H3
>  #define SUNXI_DE_FE0_BASE		0x01e00000
> +#else
> +#define SUNXI_TVE0_BASE			0x01e00000
> +#endif

This one should be in that other patch

>  #define SUNXI_DE_FE1_BASE		0x01e20000
>  #define SUNXI_DE_BE0_BASE		0x01e60000
> +#ifndef CONFIG_MACH_SUN50I_H5
>  #define SUNXI_DE_BE1_BASE		0x01e40000
> +#else
> +#define SUNXI_TVE0_BASE			0x01e40000
> +#endif

And that one too.

>  #define SUNXI_MP_BASE			0x01e80000
>  #define SUNXI_AVG_BASE			0x01ea0000
>  
> diff --git a/arch/arm/include/asm/arch-sunxi/display2.h b/arch/arm/include/asm/arch-sunxi/display2.h
> index b5875f9605..359cacd90b 100644
> --- a/arch/arm/include/asm/arch-sunxi/display2.h
> +++ b/arch/arm/include/asm/arch-sunxi/display2.h
> @@ -90,6 +90,23 @@ struct de_ui {
>  	u32 ovl_size;
>  };
>  
> +struct de_csc {
> +	u32 csc_ctl;
> +	u8 res[0xc];
> +	u32 coef11;
> +	u32 coef12;
> +	u32 coef13;
> +	u32 coef14;
> +	u32 coef21;
> +	u32 coef22;
> +	u32 coef23;
> +	u32 coef24;
> +	u32 coef31;
> +	u32 coef32;
> +	u32 coef33;
> +	u32 coef34;
> +};
> +

This should be in another patch (let's call it patch 2)

>  /*
>   * DE register constants.
>   */
> 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 */

And these yet another one (patch 3)

>  };
>  
>  /*
> @@ -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

patch 3

>  
>  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 dbaab61b59..1db82c53f0 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.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 tve.o lcdc.o ../dw_hdmi.o
> diff --git a/drivers/video/sunxi/sunxi_de2.c b/drivers/video/sunxi/sunxi_de2.c
> index 9a32c3a020..ee67764ac5 100644
> --- a/drivers/video/sunxi/sunxi_de2.c
> +++ b/drivers/video/sunxi/sunxi_de2.c
> @@ -56,7 +56,7 @@ static void sunxi_de2_composer_init(void)
>  }
>  
>  static void sunxi_de2_mode_set(int mux, const struct display_timing *mode,
> -			       int bpp, ulong address)
> +			       int bpp, ulong address, bool is_composite)
>  {
>  	ulong de_mux_base = (mux == 0) ?
>  			    SUNXI_DE2_MUX0_BASE : SUNXI_DE2_MUX1_BASE;

patch 2

> @@ -72,6 +72,9 @@ static void sunxi_de2_mode_set(int mux, const struct display_timing *mode,
>  		(struct de_ui *)(de_mux_base +
>  				 SUNXI_DE2_MUX_CHAN_REGS +
>  				 SUNXI_DE2_MUX_CHAN_SZ * 1);
> +	struct de_csc * const de_csc_regs =
> +		(struct de_csc *)(de_mux_base +
> +				  SUNXI_DE2_MUX_DCSC_REGS);

patch 2

>  	u32 size = SUNXI_DE2_WH(mode->hactive.typ, mode->vactive.typ);
>  	int channel;
>  	u32 format;
> @@ -128,7 +131,27 @@ static void sunxi_de2_mode_set(int mux, const struct display_timing *mode,
>  	writel(0, de_mux_base + SUNXI_DE2_MUX_PEAK_REGS);
>  	writel(0, de_mux_base + SUNXI_DE2_MUX_ASE_REGS);
>  	writel(0, de_mux_base + SUNXI_DE2_MUX_FCC_REGS);
> -	writel(0, de_mux_base + SUNXI_DE2_MUX_DCSC_REGS);
> +
> +	if (is_composite) {
> +		/* set CSC coefficients */
> +		writel(0x107, &de_csc_regs->coef11);
> +		writel(0x204, &de_csc_regs->coef12);
> +		writel(0x64, &de_csc_regs->coef13);
> +		writel(0x4200, &de_csc_regs->coef14);
> +		writel(0x1f68, &de_csc_regs->coef21);
> +		writel(0x1ed6, &de_csc_regs->coef22);
> +		writel(0x1c2, &de_csc_regs->coef23);
> +		writel(0x20200, &de_csc_regs->coef24);
> +		writel(0x1c2, &de_csc_regs->coef31);
> +		writel(0x1e87, &de_csc_regs->coef32);
> +		writel(0x1fb7, &de_csc_regs->coef33);
> +		writel(0x20200, &de_csc_regs->coef34);
> +
> +		/* enable CSC unit */
> +		writel(1, &de_csc_regs->csc_ctl);
> +	} else {
> +		writel(0, &de_csc_regs->csc_ctl);
> +	}

patch 2

>  
>  	switch (bpp) {
>  	case 16:
> @@ -153,7 +176,7 @@ static void sunxi_de2_mode_set(int mux, const struct display_timing *mode,
>  
>  static int sunxi_de2_init(struct udevice *dev, ulong fbbase,
>  			  enum video_log2_bpp l2bpp,
> -			  struct udevice *disp, int mux)
> +			  struct udevice *disp, int mux, bool is_composite)
>  {
>  	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
>  	struct display_timing timing;
> @@ -183,7 +206,7 @@ static int sunxi_de2_init(struct udevice *dev, ulong fbbase,
>  	}
>  
>  	sunxi_de2_composer_init();
> -	sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase);
> +	sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase, is_composite);
>  
>  	ret = display_enable(disp, 1 << l2bpp, &timing);
>  	if (ret) {
> @@ -204,7 +227,6 @@ static int sunxi_de2_probe(struct udevice *dev)
>  	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
>  	struct udevice *disp;
>  	int ret;
> -	int mux;
>  
>  	/* Before relocation we don't need to do anything */
>  	if (!(gd->flags & GD_FLG_RELOC))
> @@ -212,17 +234,31 @@ static int sunxi_de2_probe(struct udevice *dev)
>  
>  	ret = uclass_find_device_by_name(UCLASS_DISPLAY,
>  					 "sunxi_dw_hdmi", &disp);
> +	if (!ret) {
> +		int mux;
> +		if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
> +			mux = 0;
> +		else
> +			mux = 1;
> +
> +		ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux,
> +				     false);
> +		if (!ret) {
> +			video_set_flush_dcache(dev, 1);
> +			return 0;
> +		}
> +	}
> +
> +	debug("%s: hdmi display not found (ret=%d)\n", __func__, ret);
> +
> +	ret = uclass_find_device_by_name(UCLASS_DISPLAY,
> +					"sunxi_tve", &disp);
>  	if (ret) {
> -		debug("%s: hdmi display not found (ret=%d)\n", __func__, ret);
> +		debug("%s: tv not found (ret=%d)\n", __func__, ret);
>  		return ret;
>  	}
>  
> -	if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
> -		mux = 0;
> -	else
> -		mux = 1;
> -
> -	ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux);
> +	ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, 1, true);

patch 2

>  	if (ret)
>  		return ret;
>  
> 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 <jernej.skrabec@siol.net>
> + *
> + * 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

patch 3

> diff --git a/drivers/video/sunxi/tve.c b/drivers/video/sunxi/tve.c
> index adea78a69a..ef99c111e3 100644
> --- a/drivers/video/sunxi/tve.c
> +++ b/drivers/video/sunxi/tve.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,

And patch 3 as well.

Thanks!
Maxime
Icenowy Zheng May 13, 2017, 3:02 p.m. UTC | #2
在 2017-05-13 00:06,Maxime Ripard 写道:
> Hi Jernej,
> 
> The patch content looks fine, but there's a few things that would need
> to be addressed.
> 
> On Wed, May 10, 2017 at 06:46:30PM +0200, Jernej Skrabec wrote:
>> This commit adds support for TV (composite) output.
>> 
>> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
>> ---
>> 
>>  arch/arm/include/asm/arch-sunxi/cpu_sun4i.h |  10 ++
>>  arch/arm/include/asm/arch-sunxi/display2.h  |  17 +++
>>  arch/arm/include/asm/arch-sunxi/tve.h       |  17 ++-
>>  drivers/video/sunxi/Makefile                |   2 +-
>>  drivers/video/sunxi/sunxi_de2.c             |  60 ++++++++---
>>  drivers/video/sunxi/sunxi_tve.c             | 156 
>> ++++++++++++++++++++++++++++
>>  drivers/video/sunxi/tve.c                   |   6 +-
> 
> The difference between sunxi_tve and tve is not really obvious. What
> about calling sunxi_tve tve-uclass, or something like that?
> 
>>  7 files changed, 251 insertions(+), 17 deletions(-)
>>  create mode 100644 drivers/video/sunxi/sunxi_tve.c
>> 
>> diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h 
>> b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
>> index 6aa5e91ada..2419062d45 100644
>> --- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
>> +++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
>> @@ -34,7 +34,9 @@
>>  #define SUNXI_MS_BASE			0x01c07000
>>  #define SUNXI_TVD_BASE			0x01c08000
>>  #define SUNXI_CSI0_BASE			0x01c09000
>> +#ifndef CONFIG_MACH_SUNXI_H3_H5
>>  #define SUNXI_TVE0_BASE			0x01c0a000
>> +#endif
> 
> This should be part of a seperate patch.
> 
>>  #define SUNXI_EMAC_BASE			0x01c0b000
>>  #define SUNXI_LCD0_BASE			0x01c0C000
>>  #define SUNXI_LCD1_BASE			0x01c0d000
>> @@ -161,10 +163,18 @@ defined(CONFIG_MACH_SUN50I)
>>  /* module sram */
>>  #define SUNXI_SRAM_C_BASE		0x01d00000
>> 
>> +#ifndef CONFIG_MACH_SUN8I_H3
>>  #define SUNXI_DE_FE0_BASE		0x01e00000
>> +#else
>> +#define SUNXI_TVE0_BASE			0x01e00000
>> +#endif
> 
> This one should be in that other patch
> 
>>  #define SUNXI_DE_FE1_BASE		0x01e20000
>>  #define SUNXI_DE_BE0_BASE		0x01e60000
>> +#ifndef CONFIG_MACH_SUN50I_H5
>>  #define SUNXI_DE_BE1_BASE		0x01e40000
>> +#else
>> +#define SUNXI_TVE0_BASE			0x01e40000
>> +#endif
> 
> And that one too.
> 
>>  #define SUNXI_MP_BASE			0x01e80000
>>  #define SUNXI_AVG_BASE			0x01ea0000
>> 
>> diff --git a/arch/arm/include/asm/arch-sunxi/display2.h 
>> b/arch/arm/include/asm/arch-sunxi/display2.h
>> index b5875f9605..359cacd90b 100644
>> --- a/arch/arm/include/asm/arch-sunxi/display2.h
>> +++ b/arch/arm/include/asm/arch-sunxi/display2.h
>> @@ -90,6 +90,23 @@ struct de_ui {
>>  	u32 ovl_size;
>>  };
>> 
>> +struct de_csc {
>> +	u32 csc_ctl;
>> +	u8 res[0xc];
>> +	u32 coef11;
>> +	u32 coef12;
>> +	u32 coef13;
>> +	u32 coef14;
>> +	u32 coef21;
>> +	u32 coef22;
>> +	u32 coef23;
>> +	u32 coef24;
>> +	u32 coef31;
>> +	u32 coef32;
>> +	u32 coef33;
>> +	u32 coef34;
>> +};
>> +
> 
> This should be in another patch (let's call it patch 2)
> 
>>  /*
>>   * DE register constants.
>>   */
>> 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 */
> 
> And these yet another one (patch 3)
> 
>>  };
>> 
>>  /*
>> @@ -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
> 
> patch 3
> 
>> 
>>  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 dbaab61b59..1db82c53f0 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.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 
>> tve.o lcdc.o ../dw_hdmi.o
>> diff --git a/drivers/video/sunxi/sunxi_de2.c 
>> b/drivers/video/sunxi/sunxi_de2.c
>> index 9a32c3a020..ee67764ac5 100644
>> --- a/drivers/video/sunxi/sunxi_de2.c
>> +++ b/drivers/video/sunxi/sunxi_de2.c
>> @@ -56,7 +56,7 @@ static void sunxi_de2_composer_init(void)
>>  }
>> 
>>  static void sunxi_de2_mode_set(int mux, const struct display_timing 
>> *mode,
>> -			       int bpp, ulong address)
>> +			       int bpp, ulong address, bool is_composite)
>>  {
>>  	ulong de_mux_base = (mux == 0) ?
>>  			    SUNXI_DE2_MUX0_BASE : SUNXI_DE2_MUX1_BASE;
> 
> patch 2
> 
>> @@ -72,6 +72,9 @@ static void sunxi_de2_mode_set(int mux, const struct 
>> display_timing *mode,
>>  		(struct de_ui *)(de_mux_base +
>>  				 SUNXI_DE2_MUX_CHAN_REGS +
>>  				 SUNXI_DE2_MUX_CHAN_SZ * 1);
>> +	struct de_csc * const de_csc_regs =
>> +		(struct de_csc *)(de_mux_base +
>> +				  SUNXI_DE2_MUX_DCSC_REGS);
> 
> patch 2
> 
>>  	u32 size = SUNXI_DE2_WH(mode->hactive.typ, mode->vactive.typ);
>>  	int channel;
>>  	u32 format;
>> @@ -128,7 +131,27 @@ static void sunxi_de2_mode_set(int mux, const 
>> struct display_timing *mode,
>>  	writel(0, de_mux_base + SUNXI_DE2_MUX_PEAK_REGS);
>>  	writel(0, de_mux_base + SUNXI_DE2_MUX_ASE_REGS);
>>  	writel(0, de_mux_base + SUNXI_DE2_MUX_FCC_REGS);
>> -	writel(0, de_mux_base + SUNXI_DE2_MUX_DCSC_REGS);
>> +
>> +	if (is_composite) {
>> +		/* set CSC coefficients */
>> +		writel(0x107, &de_csc_regs->coef11);
>> +		writel(0x204, &de_csc_regs->coef12);
>> +		writel(0x64, &de_csc_regs->coef13);
>> +		writel(0x4200, &de_csc_regs->coef14);
>> +		writel(0x1f68, &de_csc_regs->coef21);
>> +		writel(0x1ed6, &de_csc_regs->coef22);
>> +		writel(0x1c2, &de_csc_regs->coef23);
>> +		writel(0x20200, &de_csc_regs->coef24);
>> +		writel(0x1c2, &de_csc_regs->coef31);
>> +		writel(0x1e87, &de_csc_regs->coef32);
>> +		writel(0x1fb7, &de_csc_regs->coef33);
>> +		writel(0x20200, &de_csc_regs->coef34);
>> +
>> +		/* enable CSC unit */
>> +		writel(1, &de_csc_regs->csc_ctl);
>> +	} else {
>> +		writel(0, &de_csc_regs->csc_ctl);
>> +	}
> 
> patch 2
> 
>> 
>>  	switch (bpp) {
>>  	case 16:
>> @@ -153,7 +176,7 @@ static void sunxi_de2_mode_set(int mux, const 
>> struct display_timing *mode,
>> 
>>  static int sunxi_de2_init(struct udevice *dev, ulong fbbase,
>>  			  enum video_log2_bpp l2bpp,
>> -			  struct udevice *disp, int mux)
>> +			  struct udevice *disp, int mux, bool is_composite)
>>  {
>>  	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
>>  	struct display_timing timing;
>> @@ -183,7 +206,7 @@ static int sunxi_de2_init(struct udevice *dev, 
>> ulong fbbase,
>>  	}
>> 
>>  	sunxi_de2_composer_init();
>> -	sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase);
>> +	sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase, is_composite);
>> 
>>  	ret = display_enable(disp, 1 << l2bpp, &timing);
>>  	if (ret) {
>> @@ -204,7 +227,6 @@ static int sunxi_de2_probe(struct udevice *dev)
>>  	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
>>  	struct udevice *disp;
>>  	int ret;
>> -	int mux;
>> 
>>  	/* Before relocation we don't need to do anything */
>>  	if (!(gd->flags & GD_FLG_RELOC))
>> @@ -212,17 +234,31 @@ static int sunxi_de2_probe(struct udevice *dev)
>> 
>>  	ret = uclass_find_device_by_name(UCLASS_DISPLAY,
>>  					 "sunxi_dw_hdmi", &disp);
>> +	if (!ret) {
>> +		int mux;
>> +		if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
>> +			mux = 0;
>> +		else
>> +			mux = 1;
>> +
>> +		ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux,
>> +				     false);
>> +		if (!ret) {
>> +			video_set_flush_dcache(dev, 1);
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	debug("%s: hdmi display not found (ret=%d)\n", __func__, ret);
>> +
>> +	ret = uclass_find_device_by_name(UCLASS_DISPLAY,
>> +					"sunxi_tve", &disp);
>>  	if (ret) {
>> -		debug("%s: hdmi display not found (ret=%d)\n", __func__, ret);
>> +		debug("%s: tv not found (ret=%d)\n", __func__, ret);
>>  		return ret;
>>  	}
>> 
>> -	if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
>> -		mux = 0;
>> -	else
>> -		mux = 1;
>> -
>> -	ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux);
>> +	ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, 1, true);
> 
> patch 2
> 
>>  	if (ret)
>>  		return ret;
>> 
>> 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 <jernej.skrabec@siol.net>
>> + *
>> + * 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;

So TVE is now capable of hpd checking, right?

Is is a feature that exists in A10/A13/A20 or is it new in H3?

>> +}
>> +
>> +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
> 
> patch 3
> 
>> diff --git a/drivers/video/sunxi/tve.c b/drivers/video/sunxi/tve.c
>> index adea78a69a..ef99c111e3 100644
>> --- a/drivers/video/sunxi/tve.c
>> +++ b/drivers/video/sunxi/tve.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,
> 
> And patch 3 as well.
> 
> Thanks!
> Maxime
> 
> --
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com
Chen-Yu Tsai May 13, 2017, 3:14 p.m. UTC | #3
On Sat, May 13, 2017 at 11:02 PM,  <icenowy@aosc.io> wrote:
> 在 2017-05-13 00:06,Maxime Ripard 写道:
>>
>> Hi Jernej,
>>
>> The patch content looks fine, but there's a few things that would need
>> to be addressed.
>>
>> On Wed, May 10, 2017 at 06:46:30PM +0200, Jernej Skrabec wrote:
>>>
>>> This commit adds support for TV (composite) output.
>>>
>>> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
>>> ---
>>>
>>>  arch/arm/include/asm/arch-sunxi/cpu_sun4i.h |  10 ++
>>>  arch/arm/include/asm/arch-sunxi/display2.h  |  17 +++
>>>  arch/arm/include/asm/arch-sunxi/tve.h       |  17 ++-
>>>  drivers/video/sunxi/Makefile                |   2 +-
>>>  drivers/video/sunxi/sunxi_de2.c             |  60 ++++++++---
>>>  drivers/video/sunxi/sunxi_tve.c             | 156
>>> ++++++++++++++++++++++++++++
>>>  drivers/video/sunxi/tve.c                   |   6 +-
>>
>>
>> The difference between sunxi_tve and tve is not really obvious. What
>> about calling sunxi_tve tve-uclass, or something like that?
>>
>>>  7 files changed, 251 insertions(+), 17 deletions(-)
>>>  create mode 100644 drivers/video/sunxi/sunxi_tve.c
>>>
>>> diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
>>> b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
>>> index 6aa5e91ada..2419062d45 100644
>>> --- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
>>> +++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
>>> @@ -34,7 +34,9 @@
>>>  #define SUNXI_MS_BASE                  0x01c07000
>>>  #define SUNXI_TVD_BASE                 0x01c08000
>>>  #define SUNXI_CSI0_BASE                        0x01c09000
>>> +#ifndef CONFIG_MACH_SUNXI_H3_H5
>>>  #define SUNXI_TVE0_BASE                        0x01c0a000
>>> +#endif
>>
>>
>> This should be part of a seperate patch.
>>
>>>  #define SUNXI_EMAC_BASE                        0x01c0b000
>>>  #define SUNXI_LCD0_BASE                        0x01c0C000
>>>  #define SUNXI_LCD1_BASE                        0x01c0d000
>>> @@ -161,10 +163,18 @@ defined(CONFIG_MACH_SUN50I)
>>>  /* module sram */
>>>  #define SUNXI_SRAM_C_BASE              0x01d00000
>>>
>>> +#ifndef CONFIG_MACH_SUN8I_H3
>>>  #define SUNXI_DE_FE0_BASE              0x01e00000
>>> +#else
>>> +#define SUNXI_TVE0_BASE                        0x01e00000
>>> +#endif
>>
>>
>> This one should be in that other patch
>>
>>>  #define SUNXI_DE_FE1_BASE              0x01e20000
>>>  #define SUNXI_DE_BE0_BASE              0x01e60000
>>> +#ifndef CONFIG_MACH_SUN50I_H5
>>>  #define SUNXI_DE_BE1_BASE              0x01e40000
>>> +#else
>>> +#define SUNXI_TVE0_BASE                        0x01e40000
>>> +#endif
>>
>>
>> And that one too.
>>
>>>  #define SUNXI_MP_BASE                  0x01e80000
>>>  #define SUNXI_AVG_BASE                 0x01ea0000
>>>
>>> diff --git a/arch/arm/include/asm/arch-sunxi/display2.h
>>> b/arch/arm/include/asm/arch-sunxi/display2.h
>>> index b5875f9605..359cacd90b 100644
>>> --- a/arch/arm/include/asm/arch-sunxi/display2.h
>>> +++ b/arch/arm/include/asm/arch-sunxi/display2.h
>>> @@ -90,6 +90,23 @@ struct de_ui {
>>>         u32 ovl_size;
>>>  };
>>>
>>> +struct de_csc {
>>> +       u32 csc_ctl;
>>> +       u8 res[0xc];
>>> +       u32 coef11;
>>> +       u32 coef12;
>>> +       u32 coef13;
>>> +       u32 coef14;
>>> +       u32 coef21;
>>> +       u32 coef22;
>>> +       u32 coef23;
>>> +       u32 coef24;
>>> +       u32 coef31;
>>> +       u32 coef32;
>>> +       u32 coef33;
>>> +       u32 coef34;
>>> +};
>>> +
>>
>>
>> This should be in another patch (let's call it patch 2)
>>
>>>  /*
>>>   * DE register constants.
>>>   */
>>> 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 */
>>
>>
>> And these yet another one (patch 3)
>>
>>>  };
>>>
>>>  /*
>>> @@ -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
>>
>>
>> patch 3
>>
>>>
>>>  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 dbaab61b59..1db82c53f0 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.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 tve.o
>>> lcdc.o ../dw_hdmi.o
>>> diff --git a/drivers/video/sunxi/sunxi_de2.c
>>> b/drivers/video/sunxi/sunxi_de2.c
>>> index 9a32c3a020..ee67764ac5 100644
>>> --- a/drivers/video/sunxi/sunxi_de2.c
>>> +++ b/drivers/video/sunxi/sunxi_de2.c
>>> @@ -56,7 +56,7 @@ static void sunxi_de2_composer_init(void)
>>>  }
>>>
>>>  static void sunxi_de2_mode_set(int mux, const struct display_timing
>>> *mode,
>>> -                              int bpp, ulong address)
>>> +                              int bpp, ulong address, bool is_composite)
>>>  {
>>>         ulong de_mux_base = (mux == 0) ?
>>>                             SUNXI_DE2_MUX0_BASE : SUNXI_DE2_MUX1_BASE;
>>
>>
>> patch 2
>>
>>> @@ -72,6 +72,9 @@ static void sunxi_de2_mode_set(int mux, const struct
>>> display_timing *mode,
>>>                 (struct de_ui *)(de_mux_base +
>>>                                  SUNXI_DE2_MUX_CHAN_REGS +
>>>                                  SUNXI_DE2_MUX_CHAN_SZ * 1);
>>> +       struct de_csc * const de_csc_regs =
>>> +               (struct de_csc *)(de_mux_base +
>>> +                                 SUNXI_DE2_MUX_DCSC_REGS);
>>
>>
>> patch 2
>>
>>>         u32 size = SUNXI_DE2_WH(mode->hactive.typ, mode->vactive.typ);
>>>         int channel;
>>>         u32 format;
>>> @@ -128,7 +131,27 @@ static void sunxi_de2_mode_set(int mux, const struct
>>> display_timing *mode,
>>>         writel(0, de_mux_base + SUNXI_DE2_MUX_PEAK_REGS);
>>>         writel(0, de_mux_base + SUNXI_DE2_MUX_ASE_REGS);
>>>         writel(0, de_mux_base + SUNXI_DE2_MUX_FCC_REGS);
>>> -       writel(0, de_mux_base + SUNXI_DE2_MUX_DCSC_REGS);
>>> +
>>> +       if (is_composite) {
>>> +               /* set CSC coefficients */
>>> +               writel(0x107, &de_csc_regs->coef11);
>>> +               writel(0x204, &de_csc_regs->coef12);
>>> +               writel(0x64, &de_csc_regs->coef13);
>>> +               writel(0x4200, &de_csc_regs->coef14);
>>> +               writel(0x1f68, &de_csc_regs->coef21);
>>> +               writel(0x1ed6, &de_csc_regs->coef22);
>>> +               writel(0x1c2, &de_csc_regs->coef23);
>>> +               writel(0x20200, &de_csc_regs->coef24);
>>> +               writel(0x1c2, &de_csc_regs->coef31);
>>> +               writel(0x1e87, &de_csc_regs->coef32);
>>> +               writel(0x1fb7, &de_csc_regs->coef33);
>>> +               writel(0x20200, &de_csc_regs->coef34);
>>> +
>>> +               /* enable CSC unit */
>>> +               writel(1, &de_csc_regs->csc_ctl);
>>> +       } else {
>>> +               writel(0, &de_csc_regs->csc_ctl);
>>> +       }
>>
>>
>> patch 2
>>
>>>
>>>         switch (bpp) {
>>>         case 16:
>>> @@ -153,7 +176,7 @@ static void sunxi_de2_mode_set(int mux, const struct
>>> display_timing *mode,
>>>
>>>  static int sunxi_de2_init(struct udevice *dev, ulong fbbase,
>>>                           enum video_log2_bpp l2bpp,
>>> -                         struct udevice *disp, int mux)
>>> +                         struct udevice *disp, int mux, bool
>>> is_composite)
>>>  {
>>>         struct video_priv *uc_priv = dev_get_uclass_priv(dev);
>>>         struct display_timing timing;
>>> @@ -183,7 +206,7 @@ static int sunxi_de2_init(struct udevice *dev, ulong
>>> fbbase,
>>>         }
>>>
>>>         sunxi_de2_composer_init();
>>> -       sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase);
>>> +       sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase,
>>> is_composite);
>>>
>>>         ret = display_enable(disp, 1 << l2bpp, &timing);
>>>         if (ret) {
>>> @@ -204,7 +227,6 @@ static int sunxi_de2_probe(struct udevice *dev)
>>>         struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
>>>         struct udevice *disp;
>>>         int ret;
>>> -       int mux;
>>>
>>>         /* Before relocation we don't need to do anything */
>>>         if (!(gd->flags & GD_FLG_RELOC))
>>> @@ -212,17 +234,31 @@ static int sunxi_de2_probe(struct udevice *dev)
>>>
>>>         ret = uclass_find_device_by_name(UCLASS_DISPLAY,
>>>                                          "sunxi_dw_hdmi", &disp);
>>> +       if (!ret) {
>>> +               int mux;
>>> +               if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
>>> +                       mux = 0;
>>> +               else
>>> +                       mux = 1;
>>> +
>>> +               ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp,
>>> mux,
>>> +                                    false);
>>> +               if (!ret) {
>>> +                       video_set_flush_dcache(dev, 1);
>>> +                       return 0;
>>> +               }
>>> +       }
>>> +
>>> +       debug("%s: hdmi display not found (ret=%d)\n", __func__, ret);
>>> +
>>> +       ret = uclass_find_device_by_name(UCLASS_DISPLAY,
>>> +                                       "sunxi_tve", &disp);
>>>         if (ret) {
>>> -               debug("%s: hdmi display not found (ret=%d)\n", __func__,
>>> ret);
>>> +               debug("%s: tv not found (ret=%d)\n", __func__, ret);
>>>                 return ret;
>>>         }
>>>
>>> -       if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
>>> -               mux = 0;
>>> -       else
>>> -               mux = 1;
>>> -
>>> -       ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux);
>>> +       ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, 1,
>>> true);
>>
>>
>> patch 2
>>
>>>         if (ret)
>>>                 return ret;
>>>
>>> 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 <jernej.skrabec@siol.net>
>>> + *
>>> + * 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;
>
>
> So TVE is now capable of hpd checking, right?
>
> Is is a feature that exists in A10/A13/A20 or is it new in H3?

AFAIK this is also available in the earlier SoCs.
Maxime mentioned that it was unreliable though.

ChenYu

>
>>> +}
>>> +
>>> +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
>>
>>
>> patch 3
>>
>>> diff --git a/drivers/video/sunxi/tve.c b/drivers/video/sunxi/tve.c
>>> index adea78a69a..ef99c111e3 100644
>>> --- a/drivers/video/sunxi/tve.c
>>> +++ b/drivers/video/sunxi/tve.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,
>>
>>
>> And patch 3 as well.
>>
>> Thanks!
>> Maxime
>>
>> --
>> Maxime Ripard, Free Electrons
>> Embedded Linux and Kernel engineering
>> http://free-electrons.com
>
>
> --
> 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 linux-sunxi+unsubscribe@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Simon Glass May 15, 2017, 3:03 a.m. UTC | #4
Hi,

On 12 May 2017 at 10:06, Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> Hi Jernej,
>
> The patch content looks fine, but there's a few things that would need
> to be addressed.
>
> On Wed, May 10, 2017 at 06:46:30PM +0200, Jernej Skrabec wrote:
>> This commit adds support for TV (composite) output.
>>
>> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
>> ---
>>
>>  arch/arm/include/asm/arch-sunxi/cpu_sun4i.h |  10 ++
>>  arch/arm/include/asm/arch-sunxi/display2.h  |  17 +++
>>  arch/arm/include/asm/arch-sunxi/tve.h       |  17 ++-
>>  drivers/video/sunxi/Makefile                |   2 +-
>>  drivers/video/sunxi/sunxi_de2.c             |  60 ++++++++---
>>  drivers/video/sunxi/sunxi_tve.c             | 156 ++++++++++++++++++++++++++++
>>  drivers/video/sunxi/tve.c                   |   6 +-
>
> The difference between sunxi_tve and tve is not really obvious. What
> about calling sunxi_tve tve-uclass, or something like that?

That name is reserved for actual uclasses.

Regards,
Simon
Maxime Ripard May 15, 2017, 6:11 a.m. UTC | #5
On Sun, May 14, 2017 at 09:03:19PM -0600, Simon Glass wrote:
> Hi,
> 
> On 12 May 2017 at 10:06, Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> > Hi Jernej,
> >
> > The patch content looks fine, but there's a few things that would need
> > to be addressed.
> >
> > On Wed, May 10, 2017 at 06:46:30PM +0200, Jernej Skrabec wrote:
> >> This commit adds support for TV (composite) output.
> >>
> >> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> >> ---
> >>
> >>  arch/arm/include/asm/arch-sunxi/cpu_sun4i.h |  10 ++
> >>  arch/arm/include/asm/arch-sunxi/display2.h  |  17 +++
> >>  arch/arm/include/asm/arch-sunxi/tve.h       |  17 ++-
> >>  drivers/video/sunxi/Makefile                |   2 +-
> >>  drivers/video/sunxi/sunxi_de2.c             |  60 ++++++++---
> >>  drivers/video/sunxi/sunxi_tve.c             | 156 ++++++++++++++++++++++++++++
> >>  drivers/video/sunxi/tve.c                   |   6 +-
> >
> > The difference between sunxi_tve and tve is not really obvious. What
> > about calling sunxi_tve tve-uclass, or something like that?
> 
> That name is reserved for actual uclasses.

Ok. How are the driver-part usually called?

Thanks,
Maxime
Maxime Ripard May 15, 2017, 6:31 a.m. UTC | #6
On Sat, May 13, 2017 at 11:14:00PM +0800, Chen-Yu Tsai wrote:
> >>> +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;
> >
> >
> > So TVE is now capable of hpd checking, right?
> >
> > Is is a feature that exists in A10/A13/A20 or is it new in H3?
> 
> AFAIK this is also available in the earlier SoCs.
> Maxime mentioned that it was unreliable though.

This was supposed to be there, but those registers were
undocumented. It would be nice to try to see if it works.

Maxime
Jernej Škrabec May 15, 2017, 7:47 p.m. UTC | #7
Hi,

Dne ponedeljek, 15. maj 2017 ob 08:11:55 CEST je Maxime Ripard napisal(a):
> On Sun, May 14, 2017 at 09:03:19PM -0600, Simon Glass wrote:
> > Hi,
> > 
> > On 12 May 2017 at 10:06, Maxime Ripard <maxime.ripard@free-electrons.com> 
wrote:
> > > Hi Jernej,
> > > 
> > > The patch content looks fine, but there's a few things that would need
> > > to be addressed.
> > > 
> > > On Wed, May 10, 2017 at 06:46:30PM +0200, Jernej Skrabec wrote:
> > >> This commit adds support for TV (composite) output.
> > >> 
> > >> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> > >> ---
> > >> 
> > >>  arch/arm/include/asm/arch-sunxi/cpu_sun4i.h |  10 ++
> > >>  arch/arm/include/asm/arch-sunxi/display2.h  |  17 +++
> > >>  arch/arm/include/asm/arch-sunxi/tve.h       |  17 ++-
> > >>  drivers/video/sunxi/Makefile                |   2 +-
> > >>  drivers/video/sunxi/sunxi_de2.c             |  60 ++++++++---
> > >>  drivers/video/sunxi/sunxi_tve.c             | 156
> > >>  ++++++++++++++++++++++++++++ drivers/video/sunxi/tve.c               
> > >>     |   6 +-
> > > 
> > > The difference between sunxi_tve and tve is not really obvious. What
> > > about calling sunxi_tve tve-uclass, or something like that?
> > 
> > That name is reserved for actual uclasses.
> 
> Ok. How are the driver-part usually called?

I wanted to suggest that common part should be renamed to tve_common.c, but 
Anatolij already merged the patch.

Best regards,
Jernej
Anatolij Gustschin May 15, 2017, 8 p.m. UTC | #8
Hi,

On Mon, 15 May 2017 21:47:57 +0200
Jernej Škrabec jernej.skrabec@siol.net wrote:
...
> > > >>  ++++++++++++++++++++++++++++ drivers/video/sunxi/tve.c               
> > > >>     |   6 +-  
> > > > 
> > > > The difference between sunxi_tve and tve is not really obvious. What
> > > > about calling sunxi_tve tve-uclass, or something like that?  
> > > 
> > > That name is reserved for actual uclasses.  
> > 
> > Ok. How are the driver-part usually called?  
> 
> I wanted to suggest that common part should be renamed to tve_common.c, but 
> Anatolij already merged the patch.

I'm ok with renaming it to tve_common.c. If no one has any
objections, please send a renaming patch and base the rework
of patch 3/3 on it. Thanks!

--
Anatolij
Simon Glass May 15, 2017, 8:05 p.m. UTC | #9
Hi Maxime,

On 15 May 2017 at 00:11, Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> On Sun, May 14, 2017 at 09:03:19PM -0600, Simon Glass wrote:
>> Hi,
>>
>> On 12 May 2017 at 10:06, Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
>> > Hi Jernej,
>> >
>> > The patch content looks fine, but there's a few things that would need
>> > to be addressed.
>> >
>> > On Wed, May 10, 2017 at 06:46:30PM +0200, Jernej Skrabec wrote:
>> >> This commit adds support for TV (composite) output.
>> >>
>> >> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
>> >> ---
>> >>
>> >>  arch/arm/include/asm/arch-sunxi/cpu_sun4i.h |  10 ++
>> >>  arch/arm/include/asm/arch-sunxi/display2.h  |  17 +++
>> >>  arch/arm/include/asm/arch-sunxi/tve.h       |  17 ++-
>> >>  drivers/video/sunxi/Makefile                |   2 +-
>> >>  drivers/video/sunxi/sunxi_de2.c             |  60 ++++++++---
>> >>  drivers/video/sunxi/sunxi_tve.c             | 156 ++++++++++++++++++++++++++++
>> >>  drivers/video/sunxi/tve.c                   |   6 +-
>> >
>> > The difference between sunxi_tve and tve is not really obvious. What
>> > about calling sunxi_tve tve-uclass, or something like that?
>>
>> That name is reserved for actual uclasses.
>
> Ok. How are the driver-part usually called?

Well, something without the word 'uclass', and ideally using
underscores instead of hyphens.

Regards,
Simon
Jernej Škrabec May 15, 2017, 8:10 p.m. UTC | #10
Hi,

Dne ponedeljek, 15. maj 2017 ob 08:31:22 CEST je Maxime Ripard napisal(a):
> On Sat, May 13, 2017 at 11:14:00PM +0800, Chen-Yu Tsai wrote:
> > >>> +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;
> > > 
> > > So TVE is now capable of hpd checking, right?
> > > 
> > > Is is a feature that exists in A10/A13/A20 or is it new in H3?
> > 
> > AFAIK this is also available in the earlier SoCs.
> > Maxime mentioned that it was unreliable though.
> 
> This was supposed to be there, but those registers were
> undocumented. It would be nice to try to see if it works.

AFAIK, H3 and newer SoCs with TV out have two additional registers for hot 
plug detection. Here are named SUNXI_TVE_AUTO_DETECT_CFG0 and 
SUNXI_TVE_AUTO_DETECT_CFG1 and they are somewhat explained in R40 manual.

I'm not sure if this changes reliability a lot or not.

Best regards,
Jernej
diff mbox

Patch

diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
index 6aa5e91ada..2419062d45 100644
--- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
+++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h
@@ -34,7 +34,9 @@ 
 #define SUNXI_MS_BASE			0x01c07000
 #define SUNXI_TVD_BASE			0x01c08000
 #define SUNXI_CSI0_BASE			0x01c09000
+#ifndef CONFIG_MACH_SUNXI_H3_H5
 #define SUNXI_TVE0_BASE			0x01c0a000
+#endif
 #define SUNXI_EMAC_BASE			0x01c0b000
 #define SUNXI_LCD0_BASE			0x01c0C000
 #define SUNXI_LCD1_BASE			0x01c0d000
@@ -161,10 +163,18 @@  defined(CONFIG_MACH_SUN50I)
 /* module sram */
 #define SUNXI_SRAM_C_BASE		0x01d00000
 
+#ifndef CONFIG_MACH_SUN8I_H3
 #define SUNXI_DE_FE0_BASE		0x01e00000
+#else
+#define SUNXI_TVE0_BASE			0x01e00000
+#endif
 #define SUNXI_DE_FE1_BASE		0x01e20000
 #define SUNXI_DE_BE0_BASE		0x01e60000
+#ifndef CONFIG_MACH_SUN50I_H5
 #define SUNXI_DE_BE1_BASE		0x01e40000
+#else
+#define SUNXI_TVE0_BASE			0x01e40000
+#endif
 #define SUNXI_MP_BASE			0x01e80000
 #define SUNXI_AVG_BASE			0x01ea0000
 
diff --git a/arch/arm/include/asm/arch-sunxi/display2.h b/arch/arm/include/asm/arch-sunxi/display2.h
index b5875f9605..359cacd90b 100644
--- a/arch/arm/include/asm/arch-sunxi/display2.h
+++ b/arch/arm/include/asm/arch-sunxi/display2.h
@@ -90,6 +90,23 @@  struct de_ui {
 	u32 ovl_size;
 };
 
+struct de_csc {
+	u32 csc_ctl;
+	u8 res[0xc];
+	u32 coef11;
+	u32 coef12;
+	u32 coef13;
+	u32 coef14;
+	u32 coef21;
+	u32 coef22;
+	u32 coef23;
+	u32 coef24;
+	u32 coef31;
+	u32 coef32;
+	u32 coef33;
+	u32 coef34;
+};
+
 /*
  * DE register constants.
  */
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 dbaab61b59..1db82c53f0 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.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 tve.o lcdc.o ../dw_hdmi.o
diff --git a/drivers/video/sunxi/sunxi_de2.c b/drivers/video/sunxi/sunxi_de2.c
index 9a32c3a020..ee67764ac5 100644
--- a/drivers/video/sunxi/sunxi_de2.c
+++ b/drivers/video/sunxi/sunxi_de2.c
@@ -56,7 +56,7 @@  static void sunxi_de2_composer_init(void)
 }
 
 static void sunxi_de2_mode_set(int mux, const struct display_timing *mode,
-			       int bpp, ulong address)
+			       int bpp, ulong address, bool is_composite)
 {
 	ulong de_mux_base = (mux == 0) ?
 			    SUNXI_DE2_MUX0_BASE : SUNXI_DE2_MUX1_BASE;
@@ -72,6 +72,9 @@  static void sunxi_de2_mode_set(int mux, const struct display_timing *mode,
 		(struct de_ui *)(de_mux_base +
 				 SUNXI_DE2_MUX_CHAN_REGS +
 				 SUNXI_DE2_MUX_CHAN_SZ * 1);
+	struct de_csc * const de_csc_regs =
+		(struct de_csc *)(de_mux_base +
+				  SUNXI_DE2_MUX_DCSC_REGS);
 	u32 size = SUNXI_DE2_WH(mode->hactive.typ, mode->vactive.typ);
 	int channel;
 	u32 format;
@@ -128,7 +131,27 @@  static void sunxi_de2_mode_set(int mux, const struct display_timing *mode,
 	writel(0, de_mux_base + SUNXI_DE2_MUX_PEAK_REGS);
 	writel(0, de_mux_base + SUNXI_DE2_MUX_ASE_REGS);
 	writel(0, de_mux_base + SUNXI_DE2_MUX_FCC_REGS);
-	writel(0, de_mux_base + SUNXI_DE2_MUX_DCSC_REGS);
+
+	if (is_composite) {
+		/* set CSC coefficients */
+		writel(0x107, &de_csc_regs->coef11);
+		writel(0x204, &de_csc_regs->coef12);
+		writel(0x64, &de_csc_regs->coef13);
+		writel(0x4200, &de_csc_regs->coef14);
+		writel(0x1f68, &de_csc_regs->coef21);
+		writel(0x1ed6, &de_csc_regs->coef22);
+		writel(0x1c2, &de_csc_regs->coef23);
+		writel(0x20200, &de_csc_regs->coef24);
+		writel(0x1c2, &de_csc_regs->coef31);
+		writel(0x1e87, &de_csc_regs->coef32);
+		writel(0x1fb7, &de_csc_regs->coef33);
+		writel(0x20200, &de_csc_regs->coef34);
+
+		/* enable CSC unit */
+		writel(1, &de_csc_regs->csc_ctl);
+	} else {
+		writel(0, &de_csc_regs->csc_ctl);
+	}
 
 	switch (bpp) {
 	case 16:
@@ -153,7 +176,7 @@  static void sunxi_de2_mode_set(int mux, const struct display_timing *mode,
 
 static int sunxi_de2_init(struct udevice *dev, ulong fbbase,
 			  enum video_log2_bpp l2bpp,
-			  struct udevice *disp, int mux)
+			  struct udevice *disp, int mux, bool is_composite)
 {
 	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 	struct display_timing timing;
@@ -183,7 +206,7 @@  static int sunxi_de2_init(struct udevice *dev, ulong fbbase,
 	}
 
 	sunxi_de2_composer_init();
-	sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase);
+	sunxi_de2_mode_set(mux, &timing, 1 << l2bpp, fbbase, is_composite);
 
 	ret = display_enable(disp, 1 << l2bpp, &timing);
 	if (ret) {
@@ -204,7 +227,6 @@  static int sunxi_de2_probe(struct udevice *dev)
 	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
 	struct udevice *disp;
 	int ret;
-	int mux;
 
 	/* Before relocation we don't need to do anything */
 	if (!(gd->flags & GD_FLG_RELOC))
@@ -212,17 +234,31 @@  static int sunxi_de2_probe(struct udevice *dev)
 
 	ret = uclass_find_device_by_name(UCLASS_DISPLAY,
 					 "sunxi_dw_hdmi", &disp);
+	if (!ret) {
+		int mux;
+		if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
+			mux = 0;
+		else
+			mux = 1;
+
+		ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux,
+				     false);
+		if (!ret) {
+			video_set_flush_dcache(dev, 1);
+			return 0;
+		}
+	}
+
+	debug("%s: hdmi display not found (ret=%d)\n", __func__, ret);
+
+	ret = uclass_find_device_by_name(UCLASS_DISPLAY,
+					"sunxi_tve", &disp);
 	if (ret) {
-		debug("%s: hdmi display not found (ret=%d)\n", __func__, ret);
+		debug("%s: tv not found (ret=%d)\n", __func__, ret);
 		return ret;
 	}
 
-	if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
-		mux = 0;
-	else
-		mux = 1;
-
-	ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, mux);
+	ret = sunxi_de2_init(dev, plat->base, VIDEO_BPP32, disp, 1, true);
 	if (ret)
 		return ret;
 
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 <jernej.skrabec@siol.net>
+ *
+ * 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.c b/drivers/video/sunxi/tve.c
index adea78a69a..ef99c111e3 100644
--- a/drivers/video/sunxi/tve.c
+++ b/drivers/video/sunxi/tve.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,