From 88cbcae36677a42e8da71860450149ed13e58f41 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 20 Jan 2017 01:21:02 +0800 Subject: [PATCH] sunxi: display: add simplefb support for V3s SoC V3s SoC features a DE2 composer. Add support for it. Signed-off-by: Icenowy Zheng --- arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 7 +- arch/arm/include/asm/arch-sunxi/cpu_sun4i.h | 2 + arch/arm/include/asm/arch-sunxi/display.h | 119 ++++++++++++ arch/arm/include/asm/arch-sunxi/gpio.h | 2 + arch/arm/include/asm/arch-sunxi/pwm.h | 5 + arch/arm/mach-sunxi/clock_sun6i.c | 12 ++ board/sunxi/Kconfig | 1 + drivers/gpio/sunxi_gpio.c | 1 + drivers/video/sunxi_display.c | 175 ++++++++++++++++-- 9 files changed, 303 insertions(+), 21 deletions(-) diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 775943688f..4c494481c5 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -449,9 +449,13 @@ struct sunxi_ccm_reg { /* CCM bits common to all Display Engine 2.0 clock ctrl regs */ #define CCM_DE2_CTRL_M(n) ((((n) - 1) & 0xf) << 0) #define CCM_DE2_CTRL_PLL_MASK (3 << 24) +#ifndef CONFIG_MACH_SUN8I_V3S #define CCM_DE2_CTRL_PLL6_2X (0 << 24) #define CCM_DE2_CTRL_PLL10 (1 << 24) -#define CCM_DE2_CTRL_PLL3 (1 << 24) /* for V3s */ +#else +#define CCM_DE2_CTRL_PLL3 (0 << 24) /* for V3s */ +#define CCM_DE2_CTRL_PLL6_2X (2 << 24) +#endif #define CCM_DE2_CTRL_GATE (1 << 31) /* CCU security switch, H3 only */ @@ -470,6 +474,7 @@ void clock_set_mipi_pll(unsigned int hz); unsigned int clock_get_pll3(void); unsigned int clock_get_pll6(void); unsigned int clock_get_mipi_pll(void); +void clock_set_de2_mod_clock(u32 *clk_cfg, unsigned int hz); #endif #endif /* _SUNXI_CLOCK_SUN6I_H */ diff --git a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h index 3c852224e6..5184b3013b 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/cpu_sun4i.h @@ -18,6 +18,8 @@ #define SUNXI_SRAM_D_BASE 0x00010000 /* 4 kiB */ #define SUNXI_SRAM_B_BASE 0x00020000 /* 64 kiB (secure) */ +#define SUNXI_DE2_BASE 0x01000000 + #ifdef CONFIG_MACH_SUN8I_A83T #define SUNXI_CPUCFG_BASE 0x01700000 #endif diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h index b64f310b8b..9b56fd4456 100644 --- a/arch/arm/include/asm/arch-sunxi/display.h +++ b/arch/arm/include/asm/arch-sunxi/display.h @@ -157,6 +157,89 @@ struct sunxi_de_be_reg { u32 output_color_coef[12]; /* 0x9d0 */ }; +/* + * The following structures are for DE2. + * DE2 is a new generation of "composer" in some new Allwinner SoCs, such as + * A83T, H3, V3s, A64. + */ + +/* internal clock settings */ +struct de_clk { + u32 gate_cfg; + u32 bus_cfg; + u32 rst_cfg; + u32 div_cfg; + u32 sel_cfg; +}; + +/* global control */ +struct de_glb { + u32 ctl; + u32 status; + u32 dbuff; + u32 size; +}; + +/* alpha blending */ +struct de_bld { + u32 fcolor_ctl; /* 00 */ + struct { + u32 fcolor; + u32 insize; + u32 offset; + u32 dum; + } attr[4]; + u32 dum0[15]; /* (end of clear offset) */ + u32 route; /* 80 */ + u32 premultiply; + u32 bkcolor; + u32 output_size; + u32 bld_mode[4]; + u32 dum1[4]; + u32 ck_ctl; /* b0 */ + u32 ck_cfg; + u32 dum2[2]; + u32 ck_max[4]; /* c0 */ + u32 dum3[4]; + u32 ck_min[4]; /* e0 */ + u32 dum4[3]; + u32 out_ctl; /* fc */ +}; + +/* VI channel */ +struct de_vi { + struct { + u32 attr; + u32 size; + u32 coord; + u32 pitch[3]; + u32 top_laddr[3]; + u32 bot_laddr[3]; + } cfg[4]; + u32 fcolor[4]; /* c0 */ + u32 top_haddr[3]; /* d0 */ + u32 bot_haddr[3]; /* dc */ + u32 ovl_size[2]; /* e8 */ + u32 hori[2]; /* f0 */ + u32 vert[2]; /* f8 */ +}; + +struct de_ui { + struct { + u32 attr; + u32 size; + u32 coord; + u32 pitch; + u32 top_laddr; + u32 bot_laddr; + u32 fcolor; + u32 dum; + } cfg[4]; /* 00 */ + u32 top_haddr; /* 80 */ + u32 bot_haddr; + u32 ovl_size; /* 88 */ +}; + struct sunxi_lcdc_reg { u32 ctrl; /* 0x00 */ u32 int0; /* 0x04 */ @@ -346,6 +429,42 @@ struct sunxi_tve_reg { #define SUNXI_DE_BE_LAYER_ATTR1_FMT_XRGB8888 (0x09 << 8) #define SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE 1 +/* + * DE2 register constants. + */ +#define SUNXI_DE2_MUX0_BASE (u8 *)(SUNXI_DE2_BASE + 0x100000) + +#define SUNXI_DE2_MUX_GLB_REGS 0x00000 +#define SUNXI_DE2_MUX_BLD_REGS 0x01000 +#define SUNXI_DE2_MUX_CHAN_REGS 0x02000 +#define SUNXI_DE2_MUX_CHAN_SZ 0x1000 +#define SUNXI_DE2_MUX_VSU_REGS 0x20000 +#define SUNXI_DE2_MUX_GSU1_REGS 0x30000 +#define SUNXI_DE2_MUX_GSU2_REGS 0x40000 +#define SUNXI_DE2_MUX_GSU3_REGS 0x50000 +#define SUNXI_DE2_MUX_FCE_REGS 0xa0000 +#define SUNXI_DE2_MUX_BWS_REGS 0xa2000 +#define SUNXI_DE2_MUX_LTI_REGS 0xa4000 +#define SUNXI_DE2_MUX_PEAK_REGS 0xa6000 +#define SUNXI_DE2_MUX_ASE_REGS 0xa8000 +#define SUNXI_DE2_MUX_FCC_REGS 0xaa000 +#define SUNXI_DE2_MUX_DCSC_REGS 0xb0000 + +#define SUNXI_DE2_FORMAT_ARGB_8888 0 +#define SUNXI_DE2_FORMAT_BGRA_8888 3 +#define SUNXI_DE2_FORMAT_XRGB_8888 4 +#define SUNXI_DE2_FORMAT_RGB_888 8 +#define SUNXI_DE2_FORMAT_BGR_888 9 + +#define SUNXI_DE2_MUX_GLB_CTL_RT_EN (1 << 0) + +#define SUNXI_DE2_UI_CFG_ATTR_EN (1 << 0) +#define SUNXI_DE2_UI_CFG_ATTR_ALPMOD(m) ((m & 3) << 1) +#define SUNXI_DE2_UI_CFG_ATTR_FMT(f) ((f & 0xf) << 8) +#define SUNXI_DE2_UI_CFG_ATTR_ALPHA(a) ((a & 0xff) << 24) + +#define SUNXI_DE2_WH(w, h) (((h - 1) << 16) | (w - 1)) + /* * LCDC register constants. */ diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 24f85206c8..a681bdf005 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -151,6 +151,7 @@ enum sunxi_gpio_number { #define SUN8I_H3_GPA_UART0 2 #define SUN4I_GPB_PWM 2 +#define SUN8I_V3S_GPB_PWM 2 #define SUN4I_GPB_TWI0 2 #define SUN4I_GPB_TWI1 2 #define SUN5I_GPB_TWI1 2 @@ -176,6 +177,7 @@ enum sunxi_gpio_number { #define SUN5I_GPE_SDC2 3 #define SUN8I_GPE_TWI2 3 +#define SUN8I_V3S_GPE_LCD 3 #define SUNXI_GPF_SDC0 2 #define SUNXI_GPF_UART0 4 diff --git a/arch/arm/include/asm/arch-sunxi/pwm.h b/arch/arm/include/asm/arch-sunxi/pwm.h index 5884b5dbe7..c83ca377b4 100644 --- a/arch/arm/include/asm/arch-sunxi/pwm.h +++ b/arch/arm/include/asm/arch-sunxi/pwm.h @@ -31,4 +31,9 @@ #define SUNXI_PWM_MUX SUN8I_GPH_PWM #endif +#if defined CONFIG_MACH_SUN8I_V3S +#define SUNXI_PWM_PIN0 SUNXI_GPB(4) +#define SUNXI_PWM_MUX SUN8I_V3S_GPB_PWM +#endif + #endif diff --git a/arch/arm/mach-sunxi/clock_sun6i.c b/arch/arm/mach-sunxi/clock_sun6i.c index 2a5db8f302..e8f8a30310 100644 --- a/arch/arm/mach-sunxi/clock_sun6i.c +++ b/arch/arm/mach-sunxi/clock_sun6i.c @@ -317,3 +317,15 @@ void clock_set_de_mod_clock(u32 *clk_cfg, unsigned int hz) writel(CCM_DE_CTRL_GATE | CCM_DE_CTRL_PLL6_2X | CCM_DE_CTRL_M(div), clk_cfg); } + +void clock_set_de2_mod_clock(u32 *clk_cfg, unsigned int hz) +{ + int pll = clock_get_pll6() * 2; + int div = 1; + + while ((pll / div) > hz) + div++; + + writel(CCM_DE2_CTRL_GATE | CCM_DE2_CTRL_PLL6_2X | CCM_DE2_CTRL_M(div), + clk_cfg); +} diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index ed79563f84..a451f4797e 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -136,6 +136,7 @@ config MACH_SUN8I_V3S select SUPPORT_SPL select SUNXI_H3_DW_DRAM select SUNXI_H3_DRAM_DDR2 + select SUNXI_DE2 select ARMV7_BOOT_SEC_DEFAULT if OLD_SUNXI_KERNEL_COMPAT config MACH_SUN9I diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index e8accaa333..a79907ce66 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -352,6 +352,7 @@ static const struct udevice_id sunxi_gpio_ids[] = { ID("allwinner,sun8i-a33-pinctrl", a_all), ID("allwinner,sun8i-a83t-pinctrl", a_all), ID("allwinner,sun8i-h3-pinctrl", a_all), + ID("allwinner,sun8i-v3s-pinctrl", a_all), ID("allwinner,sun9i-a80-pinctrl", a_all), ID("allwinner,sun6i-a31-r-pinctrl", l_2), ID("allwinner,sun8i-a23-r-pinctrl", l_1), diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 6f8ee01c10..a4bcc9a8a0 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -1,3 +1,5 @@ +#define DEBUG + /* * Display driver for Allwinner SoCs. * @@ -297,6 +299,26 @@ static int sunxi_hdmi_edid_get_mode(struct ctfb_res_modes *mode) #endif /* CONFIG_VIDEO_HDMI */ +static bool sunxi_is_composite(void) +{ + switch (sunxi_display.monitor) { + case sunxi_monitor_none: + case sunxi_monitor_dvi: + case sunxi_monitor_hdmi: + case sunxi_monitor_lcd: + case sunxi_monitor_vga: + return false; + case sunxi_monitor_composite_pal: + case sunxi_monitor_composite_ntsc: + case sunxi_monitor_composite_pal_m: + case sunxi_monitor_composite_pal_nc: + return true; + } + + return false; /* Never reached */ +} + +#ifndef CONFIG_SUNXI_DE2 #ifdef CONFIG_MACH_SUN4I /* * Testing has shown that on sun4i the display backend engine does not have @@ -405,25 +427,6 @@ static void sunxi_frontend_mode_set(const struct ctfb_res_modes *mode, static void sunxi_frontend_enable(void) {} #endif -static bool sunxi_is_composite(void) -{ - switch (sunxi_display.monitor) { - case sunxi_monitor_none: - case sunxi_monitor_dvi: - case sunxi_monitor_hdmi: - case sunxi_monitor_lcd: - case sunxi_monitor_vga: - return false; - case sunxi_monitor_composite_pal: - case sunxi_monitor_composite_ntsc: - case sunxi_monitor_composite_pal_m: - case sunxi_monitor_composite_pal_nc: - return true; - } - - return false; /* Never reached */ -} - /* * This is the entity that mixes and matches the different layers and inputs. * Allwinner calls it the back-end, but i like composer better. @@ -512,6 +515,127 @@ static void sunxi_composer_enable(void) setbits_le32(&de_be->reg_ctrl, SUNXI_DE_BE_REG_CTRL_LOAD_REGS); setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START); } +#else /* CONFIG_SUNXI_DE2 */ +/* + * This is the entity that mixes and matches the different layers and inputs. + * Allwinner calls it display engine (DE 2.0), but here is called composer. + */ +static void sunxi_composer_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + +#ifndef CONFIG_MACH_SUN8I_V3S + clock_set_pll10(432000000); + + /* Set DE parent to pll10 */ + clrsetbits_le32(&ccm->de_clk_cfg, CCM_DE2_CTRL_PLL_MASK, + CCM_DE2_CTRL_PLL10); +#else + clock_set_de2_mod_clock(&ccm->de_clk_cfg, 300000000); +#endif + + /* Set ahb gating to pass */ + setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_DE); + setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_DE); + + /* Clock on */ + setbits_le32(&ccm->de_clk_cfg, CCM_DE2_CTRL_GATE); +} + +static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, + unsigned int address) +{ + struct de_clk * const de_clk_regs = + (struct de_clk *)(SUNXI_DE2_BASE); + struct de_glb * const de_glb_regs = + (struct de_glb *)(SUNXI_DE2_MUX0_BASE + + SUNXI_DE2_MUX_GLB_REGS); + struct de_bld * const de_bld_regs = + (struct de_bld *)(SUNXI_DE2_MUX0_BASE + + SUNXI_DE2_MUX_BLD_REGS); + struct de_ui * const de_ui_regs = + (struct de_ui *)(SUNXI_DE2_MUX0_BASE + + SUNXI_DE2_MUX_CHAN_REGS + + SUNXI_DE2_MUX_CHAN_SZ * 2); + u32 size = SUNXI_DE2_WH(mode->xres, mode->yres); + int channel, i; + u32 data; + + /* enable clock */ + setbits_le32(&de_clk_regs->rst_cfg, 1); + setbits_le32(&de_clk_regs->gate_cfg, 1); + setbits_le32(&de_clk_regs->bus_cfg, 1); + + clrbits_le32(&de_clk_regs->sel_cfg, 1); + + writel(SUNXI_DE2_MUX_GLB_CTL_RT_EN, &de_glb_regs->ctl); + writel(0, &de_glb_regs->status); + writel(1, &de_glb_regs->dbuff); + writel(size, &de_glb_regs->size); + + for (channel = 0; channel < 4; channel++) { + void *chan = SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_CHAN_REGS + + SUNXI_DE2_MUX_CHAN_SZ * channel; + memset(chan, 0, channel == 0 ? + sizeof(struct de_vi) : sizeof(struct de_ui)); + } + memset(de_bld_regs, 0, sizeof(struct de_bld)); + + writel(0x00000101, &de_bld_regs->fcolor_ctl); + + writel(2, &de_bld_regs->route); + + writel(0, &de_bld_regs->premultiply); + writel(0xff000000, &de_bld_regs->bkcolor); + + writel(0x03010301, &de_bld_regs->bld_mode[0]); + writel(0x03010301, &de_bld_regs->bld_mode[1]); + + writel(size, &de_bld_regs->output_size); + writel(mode->vmode & FB_VMODE_INTERLACED ? 2 : 0, + &de_bld_regs->out_ctl); + writel(0, &de_bld_regs->ck_ctl); + + for (i = 0; i < 4; i++) { + writel(0xff000000, &de_bld_regs->attr[i].fcolor); + writel(size, &de_bld_regs->attr[i].insize); + } + + /* Disable all other units */ + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_VSU_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_GSU1_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_GSU2_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_GSU3_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_FCE_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_BWS_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_LTI_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_PEAK_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_ASE_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_FCC_REGS); + writel(0, SUNXI_DE2_MUX0_BASE + SUNXI_DE2_MUX_DCSC_REGS); + + data = SUNXI_DE2_UI_CFG_ATTR_EN | + SUNXI_DE2_UI_CFG_ATTR_FMT(SUNXI_DE2_FORMAT_XRGB_8888) | + SUNXI_DE2_UI_CFG_ATTR_ALPMOD(1) | + SUNXI_DE2_UI_CFG_ATTR_ALPHA(0xff); + writel(data, &de_ui_regs->cfg[0].attr); + writel(size, &de_ui_regs->cfg[0].size); + writel(0, &de_ui_regs->cfg[0].coord); + writel(4 * mode->xres, &de_ui_regs->cfg[0].pitch); + writel(address, &de_ui_regs->cfg[0].top_laddr); + writel(size, &de_ui_regs->ovl_size); +} + +static void sunxi_composer_enable(void) +{ + struct de_glb * const de_glb_regs = + (struct de_glb *)(SUNXI_DE2_MUX0_BASE + + SUNXI_DE2_MUX_GLB_REGS); + + writel(1, &de_glb_regs->dbuff); +} +#endif /* CONFIG_SUNXI_DE2 */ /* * LCDC, what allwinner calls a CRTC, so timing controller and serializer. @@ -562,6 +686,8 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock, if (!(m & 1)) continue; + /* TCONs with DE2 do not support double clock */ +#ifndef CONFIG_SUNXI_DE2 n = (m * dotclock) / 6000; if ((n >= 9) && (n <= 127)) { value = (6000 * n) / m; @@ -573,6 +699,7 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock, best_double = 1; } } +#endif } #ifdef CONFIG_MACH_SUN6I @@ -778,6 +905,7 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; int bp, clk_delay, clk_div, clk_double, pin, total, val; +#ifndef CONFIG_MACH_SUN8I_V3S #if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) { #else @@ -793,6 +921,13 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, sunxi_gpio_set_drv(pin, 3); #endif } +#else /* CONFIG_MACH_SUN8I_V3S */ + for (pin = SUNXI_GPE(0); pin <= SUNXI_GPE(24); pin++) { + if (pin >= SUNXI_GPE(20) && pin <= SUNXI_GPE(22)) + continue; /* These pins are not LCD */ + sunxi_gpio_set_cfgpin(pin, SUN8I_V3S_GPE_LCD); + } +#endif /* !CONFIG_MACH_SUN8I_V3S */ sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double); @@ -1141,7 +1276,7 @@ static void sunxi_tvencoder_enable(void) static void sunxi_drc_init(void) { -#ifdef CONFIG_SUNXI_GEN_SUN6I +#if defined CONFIG_SUNXI_GEN_SUN6I && !defined CONFIG_SUNXI_DE2 struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;