
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.