Message ID | 20170510164630.22775-4-jernej.skrabec@siol.net |
---|---|
State | Superseded |
Delegated to: | Anatolij Gustschin |
Headers | show |
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
在 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
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.
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
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
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
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
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
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
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 --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,
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