|
@@ -2,7 +2,7 @@
|
|
|
* Display driver for Allwinner SoCs.
|
|
|
*
|
|
|
* (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
|
|
|
- * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
|
|
|
+ * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
|
|
|
*
|
|
|
* 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 */
|