]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/video/sunxi_display.c
sunxi: display: Add composite video out support
[karo-tx-uboot.git] / drivers / video / sunxi_display.c
index f3cc06e2ef370bc3053599561bf753faf4f98bbb..18681850587c61e9bddb638c8b38db520a2383c7 100644 (file)
@@ -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 */