diff mbox

[U-Boot,1/3] sunxi: video: Add lvds support

Message ID 1420401940-15136-1-git-send-email-hdegoede@redhat.com
State Accepted
Delegated to: Ian Campbell
Headers show

Commit Message

Hans de Goede Jan. 4, 2015, 8:05 p.m. UTC
Add support for lvds lcd panels

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 arch/arm/include/asm/arch-sunxi/clock_sun4i.h |  2 ++
 arch/arm/include/asm/arch-sunxi/display.h     | 12 ++++++++++
 arch/arm/include/asm/arch-sunxi/gpio.h        |  1 +
 board/sunxi/Kconfig                           | 14 ++++++++++++
 drivers/video/sunxi_display.c                 | 33 +++++++++++++++++++++++++--
 5 files changed, 60 insertions(+), 2 deletions(-)

Comments

Ian Campbell Jan. 7, 2015, 6:56 p.m. UTC | #1
On Sun, 2015-01-04 at 21:05 +0100, Hans de Goede wrote:
> diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
> index 8782394..fdb18a4 100644
> --- a/board/sunxi/Kconfig
> +++ b/board/sunxi/Kconfig
> @@ -345,6 +345,20 @@ 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.
>  
> +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"

I don't see any use of this choice, I suppose it is the #else case?

Are there other (third) options for LCDs? Or could this be reworded as a
boolean rather than a choice? If it's to remain a choice I think it'd be
better to have explicit #ifdef ..._PARALLEL rather than #else. Maybe an
#else /* ..._PARALLEL */ style would be an ok compromise though.

The code itself is fine by me, not that I know **** about graphics
stuff ;-)

Ian.
Hans de Goede Jan. 7, 2015, 7:58 p.m. UTC | #2
Hi,

On 07-01-15 19:56, Ian Campbell wrote:
> On Sun, 2015-01-04 at 21:05 +0100, Hans de Goede wrote:
>> diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
>> index 8782394..fdb18a4 100644
>> --- a/board/sunxi/Kconfig
>> +++ b/board/sunxi/Kconfig
>> @@ -345,6 +345,20 @@ 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.
>>
>> +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"
>
> I don't see any use of this choice, I suppose it is the #else case?

Right, it is the else case in the actual code (for now).

> Are there other (third) options for LCDs?

Yes, see the second patch in this series, and Sairhei has a tablet which
needs something similar (but different) to the second patch.

 > Or could this be reworded as a
> boolean rather than a choice? If it's to remain a choice I think it'd be
> better to have explicit #ifdef ..._PARALLEL rather than #else. Maybe an
> #else /* ..._PARALLEL */ style would be an ok compromise though.

I can make either change, looking at the original allwinner kernel code
I guess doing the #ifdef variant makes most sense. I'll respin this when
we know what to do wrt patch 2/3.

Regards,

Hans
Ian Campbell Jan. 8, 2015, 8:36 a.m. UTC | #3
On Wed, 2015-01-07 at 20:58 +0100, Hans de Goede wrote:
> Hi,
> 
> On 07-01-15 19:56, Ian Campbell wrote:
> > On Sun, 2015-01-04 at 21:05 +0100, Hans de Goede wrote:
> >> diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
> >> index 8782394..fdb18a4 100644
> >> --- a/board/sunxi/Kconfig
> >> +++ b/board/sunxi/Kconfig
> >> @@ -345,6 +345,20 @@ 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.
> >>
> >> +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"
> >
> > I don't see any use of this choice, I suppose it is the #else case?
> 
> Right, it is the else case in the actual code (for now).
> 
> > Are there other (third) options for LCDs?
> 
> Yes, see the second patch in this series,

I've replied to the second patch again because it doesn't seem right to
put the specific choice of panel under the class of panel choice label.

>  and Sairhei has a tablet which
> needs something similar (but different) to the second patch.

I can't find a reference for this, but I suppose it might imply that the
choice of panel should be a Kconfig choice too, just not the same one as
the general class of panel Kconfig choice (IYSWIM).

Ian.
diff mbox

Patch

diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
index 64b5c38..70b789e 100644
--- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
+++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h
@@ -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)
diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h
index 44a274d..f1dfde1 100644
--- a/arch/arm/include/asm/arch-sunxi/display.h
+++ b/arch/arm/include/asm/arch-sunxi/display.h
@@ -88,6 +88,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 {
@@ -241,12 +244,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.
diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h
index 9438f5a..71cc879 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 SUNXI_GPC6_SDC2		3
 
 #define SUNXI_GPD0_LCD0		2
+#define SUNXI_GPD0_LVDS0	3
 
 #define SUNXI_GPF0_SDC0		2
 
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
index 8782394..fdb18a4 100644
--- a/board/sunxi/Kconfig
+++ b/board/sunxi/Kconfig
@@ -345,6 +345,20 @@  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.
 
+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"
+
+config VIDEO_LCD_PANEL_LVDS
+	bool "Generic lvds interface LCD panel"
+
+endchoice
+
 config USB_KEYBOARD
 	boolean "Enable USB keyboard support"
 	default y
diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c
index 319e578..4b63b01 100644
--- a/drivers/video/sunxi_display.c
+++ b/drivers/video/sunxi_display.c
@@ -339,8 +339,12 @@  static void sunxi_lcdc_pll_set(int tcon, int dotclock,
 	int best_double = 0;
 
 	if (tcon == 0) {
+#ifdef CONFIG_VIDEO_LCD_PANEL_LVDS
+		min_m = max_m = 7;
+#else
 		min_m = 6;
 		max_m = 127;
+#endif
 	} else {
 		min_m = 1;
 		max_m = 15;
@@ -420,6 +424,9 @@  static void sunxi_lcdc_init(void)
 
 	/* Clock on */
 	setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_LCD0);
+#ifdef CONFIG_VIDEO_LCD_PANEL_LVDS
+	setbits_le32(&ccm->lvds_clk_cfg, CCM_LVDS_CTRL_RST);
+#endif
 
 	/* Init lcdc */
 	writel(0, &lcdc->ctrl); /* Disable tcon */
@@ -439,6 +446,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_PANEL_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 +524,11 @@  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_PANEL_LVDS
+		sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LVDS0);
+#else
 		sunxi_gpio_set_cfgpin(pin, SUNXI_GPD0_LCD0);
+#endif
 
 	sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
 
@@ -535,12 +556,16 @@  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_PANEL_LVDS
+	val = (sunxi_display.depth == 18) ? 1 : 0;
+	writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val), &lcdc->tcon0_lvds_intf);
+#else
 	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
 
 	if (sunxi_display.depth == 18 || sunxi_display.depth == 17) {
 		writel(SUNXI_LCDC_FRAME_CTRL_DITHER0, &lcdc->frame_ctrl[1]);
@@ -559,7 +584,11 @@  static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode)
 		       &lcdc->frame_ctrl[0]);
 	}
 
-	val = 0;
+#ifdef CONFIG_VIDEO_LCD_PANEL_LVDS
+	val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE60;
+#else
+	val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE0;
+#endif
 	if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
 		val |= SUNXI_LCDC_TCON_HSYNC_MASK;
 	if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))