From: Hans de Goede Date: Mon, 3 Aug 2015 17:20:26 +0000 (+0200) Subject: sunxi: display: Add composite video out support X-Git-Tag: v2015.10-rc2~164^2 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=39920c81ce4431b7ea08f5e80feb5ec8b156864e;p=u-boot sunxi: display: Add composite video out support Add composite video out support. This only gets enabled on the Mele M3 for now, since that is were it was tested. It will be enabled on more boards after testing. Signed-off-by: Hans de Goede Acked-by: Ian Campbell --- diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index c4b5a855a5..13f453733f 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -410,6 +410,13 @@ config VIDEO_VGA_EXTERNAL_DAC_EN Set the enable pin for the external VGA DAC. This takes a string in the format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H. +config VIDEO_COMPOSITE + boolean "Composite video output support" + depends on VIDEO && (MACH_SUN4I || MACH_SUN5I || MACH_SUN7I) + default n + ---help--- + Say Y here to add support for outputting composite video. + config VIDEO_LCD_MODE string "LCD panel timing details" depends on VIDEO diff --git a/configs/Mele_M3_defconfig b/configs/Mele_M3_defconfig index d49826982f..5c9796a77b 100644 --- a/configs/Mele_M3_defconfig +++ b/configs/Mele_M3_defconfig @@ -5,6 +5,7 @@ CONFIG_DRAM_CLK=384 CONFIG_MMC0_CD_PIN="PH1" CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_VIDEO_VGA=y +CONFIG_VIDEO_COMPOSITE=y CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-m3" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL=y diff --git a/doc/README.video b/doc/README.video index d0a3ad6e8d..4f7a4b5db5 100644 --- a/doc/README.video +++ b/doc/README.video @@ -40,12 +40,15 @@ requires the CONFIG_VIDEO_LCD_MODE Kconfig value to be set. The sunxi u-boot driver supports the following video-mode options: -- monitor=[none|dvi|hdmi|lcd] - Select the video output to use +- monitor=[none|dvi|hdmi|lcd|vga|composite-*] - Select the video output to use none: Disable video output. dvi/hdmi: Selects output over the hdmi connector with dvi resp. hdmi output format, if edid is used the format is automatically selected. lcd: Selects video output to a LCD screen. - vga: Selects bideo output over the VGA connector. + vga: Selects video output over the VGA connector. + composite-pal/composite-ntsc/composite-pal-m/composite-pal-nc: + Selects composite video output, note the specified resolution is + ignored with composite video output. Defaults to monitor=dvi. - hpd=[0|1] - Enable use of the hdmi HotPlug Detect feature diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index f3cc06e2ef..1868185058 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -2,7 +2,7 @@ * Display driver for Allwinner SoCs. * * (C) Copyright 2013-2014 Luc Verhaegen - * (C) Copyright 2014 Hans de Goede + * (C) Copyright 2014-2015 Hans de Goede * * SPDX-License-Identifier: GPL-2.0+ */ @@ -40,8 +40,12 @@ enum sunxi_monitor { sunxi_monitor_hdmi, sunxi_monitor_lcd, sunxi_monitor_vga, + sunxi_monitor_composite_pal, + sunxi_monitor_composite_ntsc, + sunxi_monitor_composite_pal_m, + sunxi_monitor_composite_pal_nc, }; -#define SUNXI_MONITOR_LAST sunxi_monitor_vga +#define SUNXI_MONITOR_LAST sunxi_monitor_composite_pal_nc struct sunxi_display { GraphicDevice graphic_device; @@ -50,6 +54,12 @@ struct sunxi_display { unsigned int fb_size; } sunxi_display; +const struct ctfb_res_modes composite_video_modes[2] = { + /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */ + { 720, 576, 50, 37037, 27000, 137, 5, 20, 27, 2, 2, 0, FB_VMODE_INTERLACED }, + { 720, 480, 60, 37037, 27000, 116, 20, 16, 27, 2, 2, 0, FB_VMODE_INTERLACED }, +}; + #ifdef CONFIG_VIDEO_HDMI /* @@ -390,6 +400,25 @@ 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. @@ -423,11 +452,18 @@ static void sunxi_composer_init(void) setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_ENABLE); } +static u32 sunxi_rgb2yuv_coef[12] = { + 0x00000107, 0x00000204, 0x00000064, 0x00000108, + 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808, + 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808 +}; + static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, unsigned int address) { struct sunxi_de_be_reg * const de_be = (struct sunxi_de_be_reg *)SUNXI_DE_BE0_BASE; + int i; sunxi_frontend_mode_set(mode, address); @@ -449,6 +485,14 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_DEFLICKER_ENABLE | SUNXI_DE_BE_MODE_INTERLACE_ENABLE); + + if (sunxi_is_composite()) { + writel(SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE, + &de_be->output_color_ctrl); + for (i = 0; i < 12; i++) + writel(sunxi_rgb2yuv_coef[i], + &de_be->output_color_coef[i]); + } } static void sunxi_composer_enable(void) @@ -539,6 +583,9 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock, (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X : CCM_LCD_CH1_CTRL_PLL3) | CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg); + if (sunxi_is_composite()) + setbits_le32(&ccm->lcd0_ch1_clk_cfg, + CCM_LCD_CH1_CTRL_HALF_SCLK1); } *clk_div = best_m; @@ -766,7 +813,7 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, writel(0, &lcdc->tcon0_io_tristate); } -#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA +#if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, int *clk_div, int *clk_double, bool use_portd_hvsync) @@ -827,7 +874,7 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, } sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double); } -#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA */ +#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */ #ifdef CONFIG_VIDEO_HDMI @@ -941,9 +988,9 @@ static void sunxi_hdmi_enable(void) #endif /* CONFIG_VIDEO_HDMI */ -#ifdef CONFIG_VIDEO_VGA +#if defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE -static void sunxi_vga_mode_set(void) +static void sunxi_tvencoder_mode_set(void) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; @@ -953,16 +1000,75 @@ static void sunxi_vga_mode_set(void) /* Clock on */ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0); - /* Set TVE in VGA mode */ - writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | - SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | - SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl); - writel(SUNXI_TVE_CFG0_VGA, &tve->cfg0); - writel(SUNXI_TVE_DAC_CFG0_VGA, &tve->dac_cfg0); - writel(SUNXI_TVE_UNKNOWN1_VGA, &tve->unknown1); + switch (sunxi_display.monitor) { + case sunxi_monitor_vga: + writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | + SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | + SUNXI_TVE_GCTRL_DAC_INPUT(2, 3), &tve->gctrl); + writel(SUNXI_TVE_CFG0_VGA, &tve->cfg0); + writel(SUNXI_TVE_DAC_CFG0_VGA, &tve->dac_cfg0); + writel(SUNXI_TVE_UNKNOWN1_VGA, &tve->unknown1); + break; + case sunxi_monitor_composite_pal_nc: + writel(SUNXI_TVE_CHROMA_FREQ_PAL_NC, &tve->chroma_freq); + /* Fall through */ + case sunxi_monitor_composite_pal: + writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | + SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | + SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) | + SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl); + writel(SUNXI_TVE_CFG0_PAL, &tve->cfg0); + writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0); + writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter); + 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, &tve->blank_black_level); + writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1); + writel(SUNXI_TVE_CBR_LEVEL_PAL, &tve->cbr_level); + writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width); + writel(SUNXI_TVE_UNKNOWN2_PAL, &tve->unknown2); + writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num); + writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain); + writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width); + writel(SUNXI_TVE_RESYNC_NUM_PAL, &tve->resync_num); + writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para); + break; + case sunxi_monitor_composite_pal_m: + writel(SUNXI_TVE_CHROMA_FREQ_PAL_M, &tve->chroma_freq); + writel(SUNXI_TVE_COLOR_BURST_PAL_M, &tve->color_burst); + /* Fall through */ + case sunxi_monitor_composite_ntsc: + writel(SUNXI_TVE_GCTRL_DAC_INPUT(0, 1) | + SUNXI_TVE_GCTRL_DAC_INPUT(1, 2) | + SUNXI_TVE_GCTRL_DAC_INPUT(2, 3) | + SUNXI_TVE_GCTRL_DAC_INPUT(3, 4), &tve->gctrl); + writel(SUNXI_TVE_CFG0_NTSC, &tve->cfg0); + writel(SUNXI_TVE_DAC_CFG0_COMPOSITE, &tve->dac_cfg0); + writel(SUNXI_TVE_FILTER_COMPOSITE, &tve->filter); + writel(SUNXI_TVE_PORCH_NUM_NTSC, &tve->porch_num); + writel(SUNXI_TVE_LINE_NUM_NTSC, &tve->line_num); + writel(SUNXI_TVE_BLANK_BLACK_LEVEL_NTSC, &tve->blank_black_level); + writel(SUNXI_TVE_UNKNOWN1_COMPOSITE, &tve->unknown1); + writel(SUNXI_TVE_CBR_LEVEL_NTSC, &tve->cbr_level); + writel(SUNXI_TVE_BURST_PHASE_NTSC, &tve->burst_phase); + writel(SUNXI_TVE_BURST_WIDTH_COMPOSITE, &tve->burst_width); + writel(SUNXI_TVE_UNKNOWN2_NTSC, &tve->unknown2); + writel(SUNXI_TVE_SYNC_VBI_LEVEL_NTSC, &tve->sync_vbi_level); + writel(SUNXI_TVE_ACTIVE_NUM_COMPOSITE, &tve->active_num); + writel(SUNXI_TVE_CHROMA_BW_GAIN_COMP, &tve->chroma_bw_gain); + writel(SUNXI_TVE_NOTCH_WIDTH_COMPOSITE, &tve->notch_width); + writel(SUNXI_TVE_RESYNC_NUM_NTSC, &tve->resync_num); + writel(SUNXI_TVE_SLAVE_PARA_COMPOSITE, &tve->slave_para); + break; + case sunxi_monitor_none: + case sunxi_monitor_dvi: + case sunxi_monitor_hdmi: + case sunxi_monitor_lcd: + break; + } } -static void sunxi_vga_enable(void) +static void sunxi_tvencoder_enable(void) { struct sunxi_tve_reg * const tve = (struct sunxi_tve_reg *)SUNXI_TVE0_BASE; @@ -970,7 +1076,7 @@ static void sunxi_vga_enable(void) setbits_le32(&tve->gctrl, SUNXI_TVE_GCTRL_ENABLE); } -#endif /* CONFIG_VIDEO_VGA */ +#endif /* CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE */ static void sunxi_drc_init(void) { @@ -1085,16 +1191,29 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, #ifdef CONFIG_VIDEO_VGA sunxi_composer_mode_set(mode, address); sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1); - sunxi_vga_mode_set(); + sunxi_tvencoder_mode_set(); sunxi_composer_enable(); sunxi_lcdc_enable(); - sunxi_vga_enable(); + sunxi_tvencoder_enable(); #elif defined CONFIG_VIDEO_VGA_VIA_LCD sunxi_composer_mode_set(mode, address); sunxi_lcdc_tcon0_mode_set(mode, true); sunxi_composer_enable(); sunxi_lcdc_enable(); sunxi_vga_external_dac_enable(); +#endif + break; + case sunxi_monitor_composite_pal: + case sunxi_monitor_composite_ntsc: + case sunxi_monitor_composite_pal_m: + case sunxi_monitor_composite_pal_nc: +#ifdef CONFIG_VIDEO_COMPOSITE + sunxi_composer_mode_set(mode, address); + sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0); + sunxi_tvencoder_mode_set(); + sunxi_composer_enable(); + sunxi_lcdc_enable(); + sunxi_tvencoder_enable(); #endif break; } @@ -1103,11 +1222,15 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, static const char *sunxi_get_mon_desc(enum sunxi_monitor monitor) { switch (monitor) { - case sunxi_monitor_none: return "none"; - case sunxi_monitor_dvi: return "dvi"; - case sunxi_monitor_hdmi: return "hdmi"; - case sunxi_monitor_lcd: return "lcd"; - case sunxi_monitor_vga: return "vga"; + case sunxi_monitor_none: return "none"; + case sunxi_monitor_dvi: return "dvi"; + case sunxi_monitor_hdmi: return "hdmi"; + case sunxi_monitor_lcd: return "lcd"; + case sunxi_monitor_vga: return "vga"; + case sunxi_monitor_composite_pal: return "composite-pal"; + case sunxi_monitor_composite_ntsc: return "composite-ntsc"; + case sunxi_monitor_composite_pal_m: return "composite-pal-m"; + case sunxi_monitor_composite_pal_nc: return "composite-pal-nc"; } return NULL; /* never reached */ } @@ -1142,6 +1265,15 @@ static bool sunxi_has_vga(void) #endif } +static bool sunxi_has_composite(void) +{ +#ifdef CONFIG_VIDEO_COMPOSITE + return true; +#else + return false; +#endif +} + static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi) { if (allow_hdmi && sunxi_has_hdmi()) @@ -1150,6 +1282,8 @@ static enum sunxi_monitor sunxi_get_default_mon(bool allow_hdmi) return sunxi_monitor_lcd; else if (sunxi_has_vga()) return sunxi_monitor_vga; + else if (sunxi_has_composite()) + return sunxi_monitor_composite_pal; else return sunxi_monitor_none; } @@ -1234,6 +1368,22 @@ void *video_hw_init(void) } sunxi_display.depth = 18; break; + case sunxi_monitor_composite_pal: + case sunxi_monitor_composite_ntsc: + case sunxi_monitor_composite_pal_m: + case sunxi_monitor_composite_pal_nc: + if (!sunxi_has_composite()) { + printf("Composite video not supported on this board\n"); + sunxi_display.monitor = sunxi_monitor_none; + return NULL; + } + if (sunxi_display.monitor == sunxi_monitor_composite_pal || + sunxi_display.monitor == sunxi_monitor_composite_pal_nc) + mode = &composite_video_modes[0]; + else + mode = &composite_video_modes[1]; + sunxi_display.depth = 24; + break; } sunxi_display.fb_size = @@ -1302,6 +1452,12 @@ int sunxi_simplefb_setup(void *blob) pipeline = PIPELINE_PREFIX "de_be0-lcd0"; #endif break; + case sunxi_monitor_composite_pal: + case sunxi_monitor_composite_ntsc: + case sunxi_monitor_composite_pal_m: + case sunxi_monitor_composite_pal_nc: + pipeline = PIPELINE_PREFIX "de_be0-lcd0-tve0"; + break; } /* Find a prefilled simpefb node, matching out pipeline config */