sunxi: video: Add lvds support
authorHans de Goede <hdegoede@redhat.com>
Thu, 1 Jan 2015 21:04:34 +0000 (22:04 +0100)
committerHans de Goede <hdegoede@redhat.com>
Wed, 14 Jan 2015 13:56:40 +0000 (14:56 +0100)
Add support for lvds lcd panels

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Anatolij Gustschin <agust@denx.de>
arch/arm/include/asm/arch-sunxi/clock_sun4i.h
arch/arm/include/asm/arch-sunxi/display.h
arch/arm/include/asm/arch-sunxi/gpio.h
board/sunxi/Kconfig
drivers/video/sunxi_display.c

index 64b5c389e51c27ba12b74b66916134fa28f79ac1..70b789e8e8f7296030e0c2e8788d59e5c294e212 100644 (file)
@@ -284,6 +284,8 @@ struct sunxi_ccm_reg {
 /* Enable / disable both ch1 sclk1 and sclk2 at the same time */
 #define CCM_LCD_CH1_CTRL_GATE          (0x1 << 31 | 0x1 << 15)
 
+#define CCM_LVDS_CTRL_RST              (1 << 0)
+
 #define CCM_HDMI_CTRL_M(n)             ((((n) - 1) & 0xf) << 0)
 #define CCM_HDMI_CTRL_PLL_MASK         (3 << 24)
 #define CCM_HDMI_CTRL_PLL3             (0 << 24)
index 5ce355d7b0177b786ff7542e139ab5658c55df6f..2ac8a879dfb6feb5dd298c23322c87063b63d858 100644 (file)
@@ -91,6 +91,9 @@ struct sunxi_lcdc_reg {
        u8 res3[0x44];                  /* 0xac */
        u32 tcon1_io_polarity;          /* 0xf0 */
        u32 tcon1_io_tristate;          /* 0xf4 */
+       u8 res4[0x128];                 /* 0xf8 */
+       u32 lvds_ana0;                  /* 0x220 */
+       u32 lvds_ana1;                  /* 0x224 */
 };
 
 struct sunxi_hdmi_reg {
@@ -244,12 +247,21 @@ struct sunxi_tve_reg {
 #define SUNXI_LCDC_TCON0_TIMING_H_TOTAL(n)     (((n) - 1) << 16)
 #define SUNXI_LCDC_TCON0_TIMING_V_BP(n)                (((n) - 1) << 0)
 #define SUNXI_LCDC_TCON0_TIMING_V_TOTAL(n)     (((n) * 2) << 16)
+#define SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(n) ((n) << 26)
+#define SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE      (1 << 31)
+#define SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE0    (0 << 28)
+#define SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE60   (1 << 28)
+#define SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE120  (2 << 28)
 #define SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(n)     (((n) & 0x1f) << 4)
 #define SUNXI_LCDC_TCON1_CTRL_ENABLE           (1 << 31)
 #define SUNXI_LCDC_TCON1_TIMING_H_BP(n)                (((n) - 1) << 0)
 #define SUNXI_LCDC_TCON1_TIMING_H_TOTAL(n)     (((n) - 1) << 16)
 #define SUNXI_LCDC_TCON1_TIMING_V_BP(n)                (((n) - 1) << 0)
 #define SUNXI_LCDC_TCON1_TIMING_V_TOTAL(n)     (((n) * 2) << 16)
+#define SUNXI_LCDC_LVDS_ANA0                   0x3f310000
+#define SUNXI_LCDC_LVDS_ANA0_UPDATE            (1 << 22)
+#define SUNXI_LCDC_LVDS_ANA1_INIT1             (0x1f << 26 | 0x1f << 10)
+#define SUNXI_LCDC_LVDS_ANA1_INIT2             (0x1f << 16 | 0x1f << 00)
 
 /*
  * HDMI register constants.
index 9438f5a2623baeea0b059e6129e86376cbfc85d4..71cc879c2bb0ebf77e2dd7da5b7a6ee3b668ba12 100644 (file)
@@ -151,6 +151,7 @@ enum sunxi_gpio_number {
 #define SUNXI_GPC6_SDC2                3
 
 #define SUNXI_GPD0_LCD0                2
+#define SUNXI_GPD0_LVDS0       3
 
 #define SUNXI_GPF0_SDC0                2
 
index 9a0d8a2c01009608cf19e06defa8a4e6176faffe..bbe5c86e7b675568c7b9d360e46d5bab9a343164 100644 (file)
@@ -348,6 +348,33 @@ config VIDEO_LCD_BL_PWM
        Set the backlight pwm pin for the LCD panel. This takes a string in the
        format understood by sunxi_name_to_gpio, e.g. PH1 for pin 1 of port H.
 
+
+# Note only one of these may be selected at a time! But hidden choices are
+# not supported by Kconfig
+config VIDEO_LCD_IF_PARALLEL
+       bool
+
+config VIDEO_LCD_IF_LVDS
+       bool
+
+
+choice
+       prompt "LCD panel support"
+       depends on VIDEO
+       ---help---
+       Select which type of LCD panel to support.
+
+config VIDEO_LCD_PANEL_PARALLEL
+       bool "Generic parallel interface LCD panel"
+       select VIDEO_LCD_IF_PARALLEL
+
+config VIDEO_LCD_PANEL_LVDS
+       bool "Generic lvds interface LCD panel"
+       select VIDEO_LCD_IF_LVDS
+
+endchoice
+
+
 config USB_KEYBOARD
        boolean "Enable USB keyboard support"
        default y
index 82414925afd38876674f366a72c333f1e24fe488..47d820d89f67be1266a205703654068d98408a05 100644 (file)
@@ -339,8 +339,13 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock,
        int best_double = 0;
 
        if (tcon == 0) {
+#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
                min_m = 6;
                max_m = 127;
+#endif
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+               min_m = max_m = 7;
+#endif
        } else {
                min_m = 1;
                max_m = 15;
@@ -420,6 +425,9 @@ static void sunxi_lcdc_init(void)
 
        /* Clock on */
        setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+       setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
+#endif
 
        /* Init lcdc */
        writel(0, &lcdc->ctrl); /* Disable tcon */
@@ -439,6 +447,16 @@ static void sunxi_lcdc_enable(void)
                (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
 
        setbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_TCON_ENABLE);
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+       setbits_le32(&lcdc->tcon0_lvds_intf, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE);
+       setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0);
+       setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
+       udelay(2); /* delay at least 1200 ns */
+       setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT1);
+       udelay(1); /* delay at least 120 ns */
+       setbits_le32(&lcdc->lvds_ana1, SUNXI_LCDC_LVDS_ANA1_INIT2);
+       setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_UPDATE);
+#endif
 }
 
 static void sunxi_lcdc_panel_enable(void)
@@ -507,7 +525,12 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
        int bp, clk_delay, clk_div, clk_double, pin, total, val;
 
        for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++)
+#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
                sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0);
+#endif
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+               sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LVDS0);
+#endif
 
        sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
 
@@ -535,12 +558,17 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
        writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
               SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
 
+#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
        writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
               &lcdc->tcon0_timing_sync);
 
-       /* We only support hv-sync parallel lcd-s for now */
        writel(0, &lcdc->tcon0_hv_intf);
        writel(0, &lcdc->tcon0_cpu_intf);
+#endif
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+       val = (sunxi_display.depth == 18) ? 1 : 0;
+       writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val), &lcdc->tcon0_lvds_intf);
+#endif
 
        if (sunxi_display.depth == 18 || sunxi_display.depth == 16) {
                writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
@@ -559,7 +587,12 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
                       &lcdc->tcon0_frm_ctrl);
        }
 
-       val = 0;
+#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
+       val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE0;
+#endif
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+       val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE60;
+#endif
        if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
                val |= SUNXI_LCDC_TCON_HSYNC_MASK;
        if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))