diff mbox

[U-Boot,4/8] sunxi: video: Split out TCON code

Message ID 20170308233444.26172-5-jernej.skrabec@siol.net
State Superseded
Delegated to: Jagannadha Sutradharudu Teki
Headers show

Commit Message

Jernej Škrabec March 8, 2017, 11:34 p.m. UTC
TCON unit has similar layout and functionality also on newer SoCs. This
commit splits out TCON code for easier reuse later.

Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---

 arch/arm/include/asm/arch-sunxi/display.h | 103 --------------
 arch/arm/include/asm/arch-sunxi/lcdc.h    | 128 +++++++++++++++++
 drivers/video/Makefile                    |   2 +-
 drivers/video/sunxi/Makefile              |   8 ++
 drivers/video/sunxi/lcdc.c                | 205 +++++++++++++++++++++++++++
 drivers/video/{ => sunxi}/sunxi_display.c | 224 ++++++------------------------
 6 files changed, 387 insertions(+), 283 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-sunxi/lcdc.h
 create mode 100644 drivers/video/sunxi/Makefile
 create mode 100644 drivers/video/sunxi/lcdc.c
 rename drivers/video/{ => sunxi}/sunxi_display.c (86%)

Comments

Maxime Ripard March 9, 2017, 8:33 a.m. UTC | #1
Hi,

Thanks for your great work.

On Thu, Mar 09, 2017 at 12:34:40AM +0100, Jernej Skrabec wrote:
> -	writel(0, &lcdc->tcon0_io_tristate);
> +	sunxi_ctfb_mode_to_display_timing(mode, &timing);
> +	lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
> +			    sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);

I'm not sure what this sunxi_ctfb_mode_to_display_timing function is
useful for, but it's introduction and the conversion of the users
would probably need to be moved to another patch.

>  #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();
> +		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE,
> +			    sunxi_display.depth);
> +		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE, 0);

That one is suspicious. Shouldn't sunxi_composer_enable be left, and
lcdc_enable called only once?

Maxime
Jernej Škrabec March 9, 2017, 5:16 p.m. UTC | #2
Hi Maxime,

Dne četrtek, 09. marec 2017 ob 09:33:06 CET je Maxime Ripard napisal(a):
> Hi,
> 
> Thanks for your great work.
> 
> On Thu, Mar 09, 2017 at 12:34:40AM +0100, Jernej Skrabec wrote:
> > -	writel(0, &lcdc->tcon0_io_tristate);
> > +	sunxi_ctfb_mode_to_display_timing(mode, &timing);
> > +	lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
> > +			    sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
> 
> I'm not sure what this sunxi_ctfb_mode_to_display_timing function is
> useful for, but it's introduction and the conversion of the users
> would probably need to be moved to another patch.
> 

I forgot to explain this in commit message.

The thing is that current video display driver for Allwinner SoCs uses older 
framework called cfb console. This framework uses videomodes.h for timing 
related functions. In it, there is a structure called "struct ctfb_res_modes", 
which holds pixel clock, active resolution, sync times, all front/back porch 
values and so on. In contrast, DM video framework uses different structure 
"struct display_timing" which is defined in fdtdec.h and holds exactly the same 
timing informations. It is a bit strange to have two different structures for 
same type of informations, but at least conversion from ctfb timing to display 
timming is pretty straightforward, as you can see from the code.

It made more sense to me to use DM video timing structure because I expect 
that all new drivers will use this framework and I guess that older will be 
converted to use this framework too.

Should I move this change in new patch?

> >  #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();
> > +		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE,
> > +			    sunxi_display.depth);
> > +		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE, 0);
> 
> That one is suspicious. Shouldn't sunxi_composer_enable be left, and
> lcdc_enable called only once?

Uh, missed that. Probably fixup error. It will be fixed in next version.

Best regards,
Jernej Škrabec

> 
> Maxime
> 
> --
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com
Maxime Ripard March 10, 2017, 7:59 a.m. UTC | #3
Hi,

On Thu, Mar 09, 2017 at 06:16:27PM +0100, Jernej Škrabec wrote:
> Hi Maxime,
> 
> Dne četrtek, 09. marec 2017 ob 09:33:06 CET je Maxime Ripard napisal(a):
> > Hi,
> > 
> > Thanks for your great work.
> > 
> > On Thu, Mar 09, 2017 at 12:34:40AM +0100, Jernej Skrabec wrote:
> > > -	writel(0, &lcdc->tcon0_io_tristate);
> > > +	sunxi_ctfb_mode_to_display_timing(mode, &timing);
> > > +	lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
> > > +			    sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
> > 
> > I'm not sure what this sunxi_ctfb_mode_to_display_timing function is
> > useful for, but it's introduction and the conversion of the users
> > would probably need to be moved to another patch.
> > 
> 
> I forgot to explain this in commit message.
> 
> The thing is that current video display driver for Allwinner SoCs uses older 
> framework called cfb console. This framework uses videomodes.h for timing 
> related functions. In it, there is a structure called "struct ctfb_res_modes", 
> which holds pixel clock, active resolution, sync times, all front/back porch 
> values and so on. In contrast, DM video framework uses different structure 
> "struct display_timing" which is defined in fdtdec.h and holds exactly the same 
> timing informations. It is a bit strange to have two different structures for 
> same type of informations, but at least conversion from ctfb timing to display 
> timming is pretty straightforward, as you can see from the code.
> 
> It made more sense to me to use DM video timing structure because I expect 
> that all new drivers will use this framework and I guess that older will be 
> converted to use this framework too.

That definitely makes sense.

> Should I move this change in new patch?

Yes, please.

Thanks!
Maxime
diff mbox

Patch

diff --git a/arch/arm/include/asm/arch-sunxi/display.h b/arch/arm/include/asm/arch-sunxi/display.h
index b64f310b8b..93803addfb 100644
--- a/arch/arm/include/asm/arch-sunxi/display.h
+++ b/arch/arm/include/asm/arch-sunxi/display.h
@@ -157,52 +157,6 @@  struct sunxi_de_be_reg {
 	u32 output_color_coef[12];	/* 0x9d0 */
 };
 
-struct sunxi_lcdc_reg {
-	u32 ctrl;			/* 0x00 */
-	u32 int0;			/* 0x04 */
-	u32 int1;			/* 0x08 */
-	u8 res0[0x04];			/* 0x0c */
-	u32 tcon0_frm_ctrl;		/* 0x10 */
-	u32 tcon0_frm_seed[6];		/* 0x14 */
-	u32 tcon0_frm_table[4];		/* 0x2c */
-	u8 res1[4];			/* 0x3c */
-	u32 tcon0_ctrl;			/* 0x40 */
-	u32 tcon0_dclk;			/* 0x44 */
-	u32 tcon0_timing_active;	/* 0x48 */
-	u32 tcon0_timing_h;		/* 0x4c */
-	u32 tcon0_timing_v;		/* 0x50 */
-	u32 tcon0_timing_sync;		/* 0x54 */
-	u32 tcon0_hv_intf;		/* 0x58 */
-	u8 res2[0x04];			/* 0x5c */
-	u32 tcon0_cpu_intf;		/* 0x60 */
-	u32 tcon0_cpu_wr_dat;		/* 0x64 */
-	u32 tcon0_cpu_rd_dat0;		/* 0x68 */
-	u32 tcon0_cpu_rd_dat1;		/* 0x6c */
-	u32 tcon0_ttl_timing0;		/* 0x70 */
-	u32 tcon0_ttl_timing1;		/* 0x74 */
-	u32 tcon0_ttl_timing2;		/* 0x78 */
-	u32 tcon0_ttl_timing3;		/* 0x7c */
-	u32 tcon0_ttl_timing4;		/* 0x80 */
-	u32 tcon0_lvds_intf;		/* 0x84 */
-	u32 tcon0_io_polarity;		/* 0x88 */
-	u32 tcon0_io_tristate;		/* 0x8c */
-	u32 tcon1_ctrl;			/* 0x90 */
-	u32 tcon1_timing_source;	/* 0x94 */
-	u32 tcon1_timing_scale;		/* 0x98 */
-	u32 tcon1_timing_out;		/* 0x9c */
-	u32 tcon1_timing_h;		/* 0xa0 */
-	u32 tcon1_timing_v;		/* 0xa4 */
-	u32 tcon1_timing_sync;		/* 0xa8 */
-	u8 res3[0x44];			/* 0xac */
-	u32 tcon1_io_polarity;		/* 0xf0 */
-	u32 tcon1_io_tristate;		/* 0xf4 */
-	u8 res4[0x108];			/* 0xf8 */
-	u32 mux_ctrl;			/* 0x200 */
-	u8 res5[0x1c];			/* 0x204 */
-	u32 lvds_ana0;			/* 0x220 */
-	u32 lvds_ana1;			/* 0x224 */
-};
-
 struct sunxi_hdmi_reg {
 	u32 version_id;			/* 0x000 */
 	u32 ctrl;			/* 0x004 */
@@ -347,63 +301,6 @@  struct sunxi_tve_reg {
 #define SUNXI_DE_BE_OUTPUT_COLOR_CTRL_ENABLE	1
 
 /*
- * LCDC register constants.
- */
-#define SUNXI_LCDC_X(x)				(((x) - 1) << 16)
-#define SUNXI_LCDC_Y(y)				(((y) - 1) << 0)
-#define SUNXI_LCDC_TCON_VSYNC_MASK		(1 << 24)
-#define SUNXI_LCDC_TCON_HSYNC_MASK		(1 << 25)
-#define SUNXI_LCDC_CTRL_IO_MAP_MASK		(1 << 0)
-#define SUNXI_LCDC_CTRL_IO_MAP_TCON0		(0 << 0)
-#define SUNXI_LCDC_CTRL_IO_MAP_TCON1		(1 << 0)
-#define SUNXI_LCDC_CTRL_TCON_ENABLE		(1 << 31)
-#define SUNXI_LCDC_TCON0_FRM_CTRL_RGB666	((1 << 31) | (0 << 4))
-#define SUNXI_LCDC_TCON0_FRM_CTRL_RGB565	((1 << 31) | (5 << 4))
-#define SUNXI_LCDC_TCON0_FRM_SEED		0x11111111
-#define SUNXI_LCDC_TCON0_FRM_TAB0		0x01010000
-#define SUNXI_LCDC_TCON0_FRM_TAB1		0x15151111
-#define SUNXI_LCDC_TCON0_FRM_TAB2		0x57575555
-#define SUNXI_LCDC_TCON0_FRM_TAB3		0x7f7f7777
-#define SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(n)	(((n) & 0x1f) << 4)
-#define SUNXI_LCDC_TCON0_CTRL_ENABLE		(1 << 31)
-#define SUNXI_LCDC_TCON0_DCLK_DIV(n)		((n) << 0)
-#define SUNXI_LCDC_TCON0_DCLK_ENABLE		(0xf << 28)
-#define SUNXI_LCDC_TCON0_TIMING_H_BP(n)		(((n) - 1) << 0)
-#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)
-#ifdef CONFIG_SUNXI_GEN_SUN6I
-#define SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0	(1 << 20)
-#else
-#define SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0	0 /* NA */
-#endif
-#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_PHASE(x)	((x) << 28)
-#define SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(n)	(((n) & 0x1f) << 4)
-#define SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE	(1 << 20)
-#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) << 16)
-#define SUNXI_LCDC_MUX_CTRL_SRC0_MASK		(0xf << 0)
-#define SUNXI_LCDC_MUX_CTRL_SRC0(x)		((x) << 0)
-#define SUNXI_LCDC_MUX_CTRL_SRC1_MASK		(0xf << 4)
-#define SUNXI_LCDC_MUX_CTRL_SRC1(x)		((x) << 4)
-#ifdef CONFIG_SUNXI_GEN_SUN6I
-#define SUNXI_LCDC_LVDS_ANA0			0x40040320
-#define SUNXI_LCDC_LVDS_ANA0_EN_MB		(1 << 31)
-#define SUNXI_LCDC_LVDS_ANA0_DRVC		(1 << 24)
-#define SUNXI_LCDC_LVDS_ANA0_DRVD(x)		((x) << 20)
-#else
-#define SUNXI_LCDC_LVDS_ANA0			0x3f310000
-#define SUNXI_LCDC_LVDS_ANA0_UPDATE		(1 << 22)
-#endif
-#define SUNXI_LCDC_LVDS_ANA1_INIT1		(0x1f << 26 | 0x1f << 10)
-#define SUNXI_LCDC_LVDS_ANA1_INIT2		(0x1f << 16 | 0x1f << 00)
-
-/*
  * HDMI register constants.
  */
 #define SUNXI_HDMI_X(x)				(((x) - 1) << 0)
diff --git a/arch/arm/include/asm/arch-sunxi/lcdc.h b/arch/arm/include/asm/arch-sunxi/lcdc.h
new file mode 100644
index 0000000000..a751698b4f
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/lcdc.h
@@ -0,0 +1,128 @@ 
+/*
+ * Sunxi platform timing controller register and constant defines
+ *
+ * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
+ * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _LCDC_H
+#define _LCDC_H
+
+#include <fdtdec.h>
+
+struct sunxi_lcdc_reg {
+	u32 ctrl;			/* 0x00 */
+	u32 int0;			/* 0x04 */
+	u32 int1;			/* 0x08 */
+	u8 res0[0x04];			/* 0x0c */
+	u32 tcon0_frm_ctrl;		/* 0x10 */
+	u32 tcon0_frm_seed[6];		/* 0x14 */
+	u32 tcon0_frm_table[4];		/* 0x2c */
+	u8 res1[4];			/* 0x3c */
+	u32 tcon0_ctrl;			/* 0x40 */
+	u32 tcon0_dclk;			/* 0x44 */
+	u32 tcon0_timing_active;	/* 0x48 */
+	u32 tcon0_timing_h;		/* 0x4c */
+	u32 tcon0_timing_v;		/* 0x50 */
+	u32 tcon0_timing_sync;		/* 0x54 */
+	u32 tcon0_hv_intf;		/* 0x58 */
+	u8 res2[0x04];			/* 0x5c */
+	u32 tcon0_cpu_intf;		/* 0x60 */
+	u32 tcon0_cpu_wr_dat;		/* 0x64 */
+	u32 tcon0_cpu_rd_dat0;		/* 0x68 */
+	u32 tcon0_cpu_rd_dat1;		/* 0x6c */
+	u32 tcon0_ttl_timing0;		/* 0x70 */
+	u32 tcon0_ttl_timing1;		/* 0x74 */
+	u32 tcon0_ttl_timing2;		/* 0x78 */
+	u32 tcon0_ttl_timing3;		/* 0x7c */
+	u32 tcon0_ttl_timing4;		/* 0x80 */
+	u32 tcon0_lvds_intf;		/* 0x84 */
+	u32 tcon0_io_polarity;		/* 0x88 */
+	u32 tcon0_io_tristate;		/* 0x8c */
+	u32 tcon1_ctrl;			/* 0x90 */
+	u32 tcon1_timing_source;	/* 0x94 */
+	u32 tcon1_timing_scale;		/* 0x98 */
+	u32 tcon1_timing_out;		/* 0x9c */
+	u32 tcon1_timing_h;		/* 0xa0 */
+	u32 tcon1_timing_v;		/* 0xa4 */
+	u32 tcon1_timing_sync;		/* 0xa8 */
+	u8 res3[0x44];			/* 0xac */
+	u32 tcon1_io_polarity;		/* 0xf0 */
+	u32 tcon1_io_tristate;		/* 0xf4 */
+	u8 res4[0x108];			/* 0xf8 */
+	u32 mux_ctrl;			/* 0x200 */
+	u8 res5[0x1c];			/* 0x204 */
+	u32 lvds_ana0;			/* 0x220 */
+	u32 lvds_ana1;			/* 0x224 */
+};
+
+/*
+ * LCDC register constants.
+ */
+#define SUNXI_LCDC_X(x)				(((x) - 1) << 16)
+#define SUNXI_LCDC_Y(y)				(((y) - 1) << 0)
+#define SUNXI_LCDC_TCON_VSYNC_MASK		(1 << 24)
+#define SUNXI_LCDC_TCON_HSYNC_MASK		(1 << 25)
+#define SUNXI_LCDC_CTRL_IO_MAP_MASK		(1 << 0)
+#define SUNXI_LCDC_CTRL_IO_MAP_TCON0		(0 << 0)
+#define SUNXI_LCDC_CTRL_IO_MAP_TCON1		(1 << 0)
+#define SUNXI_LCDC_CTRL_TCON_ENABLE		(1 << 31)
+#define SUNXI_LCDC_TCON0_FRM_CTRL_RGB666	((1 << 31) | (0 << 4))
+#define SUNXI_LCDC_TCON0_FRM_CTRL_RGB565	((1 << 31) | (5 << 4))
+#define SUNXI_LCDC_TCON0_FRM_SEED		0x11111111
+#define SUNXI_LCDC_TCON0_FRM_TAB0		0x01010000
+#define SUNXI_LCDC_TCON0_FRM_TAB1		0x15151111
+#define SUNXI_LCDC_TCON0_FRM_TAB2		0x57575555
+#define SUNXI_LCDC_TCON0_FRM_TAB3		0x7f7f7777
+#define SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(n)	(((n) & 0x1f) << 4)
+#define SUNXI_LCDC_TCON0_CTRL_ENABLE		(1 << 31)
+#define SUNXI_LCDC_TCON0_DCLK_DIV(n)		((n) << 0)
+#define SUNXI_LCDC_TCON0_DCLK_ENABLE		(0xf << 28)
+#define SUNXI_LCDC_TCON0_TIMING_H_BP(n)		(((n) - 1) << 0)
+#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)
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+#define SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0	(1 << 20)
+#else
+#define SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0	0 /* NA */
+#endif
+#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_PHASE(x)	((x) << 28)
+#define SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(n)	(((n) & 0x1f) << 4)
+#define SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE	(1 << 20)
+#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) << 16)
+#define SUNXI_LCDC_MUX_CTRL_SRC0_MASK		(0xf << 0)
+#define SUNXI_LCDC_MUX_CTRL_SRC0(x)		((x) << 0)
+#define SUNXI_LCDC_MUX_CTRL_SRC1_MASK		(0xf << 4)
+#define SUNXI_LCDC_MUX_CTRL_SRC1(x)		((x) << 4)
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+#define SUNXI_LCDC_LVDS_ANA0			0x40040320
+#define SUNXI_LCDC_LVDS_ANA0_EN_MB		(1 << 31)
+#define SUNXI_LCDC_LVDS_ANA0_DRVC		(1 << 24)
+#define SUNXI_LCDC_LVDS_ANA0_DRVD(x)		((x) << 20)
+#else
+#define SUNXI_LCDC_LVDS_ANA0			0x3f310000
+#define SUNXI_LCDC_LVDS_ANA0_UPDATE		(1 << 22)
+#endif
+#define SUNXI_LCDC_LVDS_ANA1_INIT1		(0x1f << 26 | 0x1f << 10)
+#define SUNXI_LCDC_LVDS_ANA1_INIT2		(0x1f << 16 | 0x1f << 00)
+
+void lcdc_init(struct sunxi_lcdc_reg * const lcdc);
+void lcdc_enable(struct sunxi_lcdc_reg * const lcdc, int depth);
+void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc,
+			 const struct display_timing *mode,
+			 int clk_div, bool for_ext_vga_dac,
+			 int depth, int dclk_phase);
+void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc,
+			 const struct display_timing *mode,
+			 bool ext_hvsync, bool is_composite);
+
+#endif /* _LCDC_H */
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index db34904a9a..7230fd9570 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -51,7 +51,6 @@  obj-$(CONFIG_VIDEO_MXS) += mxsfb.o videomodes.o
 obj-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o
 obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o
 obj-$(CONFIG_VIDEO_SM501) += sm501.o
-obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o videomodes.o
 obj-$(CONFIG_VIDEO_TEGRA20) += tegra.o
 obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
 obj-$(CONFIG_VIDEO_VESA) += vesa.o
@@ -64,3 +63,4 @@  obj-${CONFIG_EXYNOS_FB} += exynos/
 obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
 
 obj-y += bridge/
+obj-y += sunxi/
diff --git a/drivers/video/sunxi/Makefile b/drivers/video/sunxi/Makefile
new file mode 100644
index 0000000000..dfc9b47a1f
--- /dev/null
+++ b/drivers/video/sunxi/Makefile
@@ -0,0 +1,8 @@ 
+#
+# (C) Copyright 2000-2007
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_VIDEO_SUNXI) += sunxi_display.o lcdc.o ../videomodes.o
diff --git a/drivers/video/sunxi/lcdc.c b/drivers/video/sunxi/lcdc.c
new file mode 100644
index 0000000000..8c8fb2e4ee
--- /dev/null
+++ b/drivers/video/sunxi/lcdc.c
@@ -0,0 +1,205 @@ 
+/*
+ * Timing controller driver for Allwinner SoCs.
+ *
+ * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
+ * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
+ * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+
+#include <asm/arch/lcdc.h>
+#include <asm/io.h>
+
+static int lcdc_get_clk_delay(const struct display_timing *mode, int tcon)
+{
+	int delay;
+
+	delay = mode->vfront_porch.typ + mode->vsync_len.typ +
+		mode->vback_porch.typ;
+	if (mode->flags & DISPLAY_FLAGS_INTERLACED)
+		delay /= 2;
+	if (tcon == 1)
+		delay -= 2;
+
+	return (delay > 30) ? 30 : delay;
+}
+
+void lcdc_init(struct sunxi_lcdc_reg * const lcdc)
+{
+	/* Init lcdc */
+	writel(0, &lcdc->ctrl); /* Disable tcon */
+	writel(0, &lcdc->int0); /* Disable all interrupts */
+
+	/* Disable tcon0 dot clock */
+	clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE);
+
+	/* Set all io lines to tristate */
+	writel(0xffffffff, &lcdc->tcon0_io_tristate);
+	writel(0xffffffff, &lcdc->tcon1_io_tristate);
+}
+
+void lcdc_enable(struct sunxi_lcdc_reg * const lcdc, int depth)
+{
+	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);
+#ifdef CONFIG_SUNXI_GEN_SUN6I
+	udelay(2); /* delay at least 1200 ns */
+	setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_EN_MB);
+	udelay(2); /* delay at least 1200 ns */
+	setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVC);
+	if (depth == 18)
+		setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0x7));
+	else
+		setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0xf));
+#else
+	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
+#endif
+}
+
+void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc,
+			 const struct display_timing *mode,
+			 int clk_div, bool for_ext_vga_dac,
+			 int depth, int dclk_phase)
+{
+	int bp, clk_delay, total, val;
+
+	/* Use tcon0 */
+	clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
+			SUNXI_LCDC_CTRL_IO_MAP_TCON0);
+
+	clk_delay = lcdc_get_clk_delay(mode, 0);
+	writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
+	       SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
+
+	writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
+	       SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
+
+	writel(SUNXI_LCDC_X(mode->hactive.typ) |
+	       SUNXI_LCDC_Y(mode->vactive.typ), &lcdc->tcon0_timing_active);
+
+	bp = mode->hsync_len.typ + mode->hback_porch.typ;
+	total = mode->hactive.typ + mode->hfront_porch.typ + bp;
+	writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
+	       SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
+
+	bp = mode->vsync_len.typ + mode->vback_porch.typ;
+	total = mode->vactive.typ + mode->vfront_porch.typ + bp;
+	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.typ) |
+	       SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon0_timing_sync);
+
+	writel(0, &lcdc->tcon0_hv_intf);
+	writel(0, &lcdc->tcon0_cpu_intf);
+#endif
+#ifdef CONFIG_VIDEO_LCD_IF_LVDS
+	val = (depth == 18) ? 1 : 0;
+	writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val) |
+	       SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0, &lcdc->tcon0_lvds_intf);
+#endif
+
+	if (depth == 18 || depth == 16) {
+		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
+		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
+		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
+		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
+		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
+		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
+		writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
+		writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
+		writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
+		writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
+		writel(((depth == 18) ?
+			SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
+			SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
+		       &lcdc->tcon0_frm_ctrl);
+	}
+
+	val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(dclk_phase);
+	if (mode->flags & DISPLAY_FLAGS_HSYNC_LOW)
+		val |= SUNXI_LCDC_TCON_HSYNC_MASK;
+	if (mode->flags & DISPLAY_FLAGS_VSYNC_LOW)
+		val |= SUNXI_LCDC_TCON_VSYNC_MASK;
+
+#ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH
+	if (for_ext_vga_dac)
+		val = 0;
+#endif
+	writel(val, &lcdc->tcon0_io_polarity);
+
+	writel(0, &lcdc->tcon0_io_tristate);
+}
+
+void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc,
+			 const struct display_timing *mode,
+			 bool ext_hvsync, bool is_composite)
+{
+	int bp, clk_delay, total, val, yres;
+
+	/* Use tcon1 */
+	clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
+			SUNXI_LCDC_CTRL_IO_MAP_TCON1);
+
+	clk_delay = lcdc_get_clk_delay(mode, 1);
+	writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
+	       ((mode->flags & DISPLAY_FLAGS_INTERLACED) ?
+			SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE : 0) |
+	       SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
+
+	yres = mode->vactive.typ;
+	if (mode->flags & DISPLAY_FLAGS_INTERLACED)
+		yres /= 2;
+	writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
+	       &lcdc->tcon1_timing_source);
+	writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
+	       &lcdc->tcon1_timing_scale);
+	writel(SUNXI_LCDC_X(mode->hactive.typ) | SUNXI_LCDC_Y(yres),
+	       &lcdc->tcon1_timing_out);
+
+	bp = mode->hsync_len.typ + mode->hback_porch.typ;
+	total = mode->hactive.typ + mode->hfront_porch.typ + bp;
+	writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
+	       SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
+
+	bp = mode->vsync_len.typ + mode->vback_porch.typ;
+	total = mode->vactive.typ + mode->vfront_porch.typ + bp;
+	if (!(mode->flags & DISPLAY_FLAGS_INTERLACED))
+		total *= 2;
+	writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
+	       SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
+
+	writel(SUNXI_LCDC_X(mode->hsync_len.typ) |
+	       SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon1_timing_sync);
+
+	if (ext_hvsync) {
+		val = 0;
+		if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+			val |= SUNXI_LCDC_TCON_HSYNC_MASK;
+		if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+			val |= SUNXI_LCDC_TCON_VSYNC_MASK;
+		writel(val, &lcdc->tcon1_io_polarity);
+
+		clrbits_le32(&lcdc->tcon1_io_tristate,
+			     SUNXI_LCDC_TCON_VSYNC_MASK |
+			     SUNXI_LCDC_TCON_HSYNC_MASK);
+	}
+
+#ifdef CONFIG_MACH_SUN5I
+	if (is_composite)
+		clrsetbits_le32(&lcdc->mux_ctrl, SUNXI_LCDC_MUX_CTRL_SRC0_MASK,
+				SUNXI_LCDC_MUX_CTRL_SRC0(1));
+#endif
+}
diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi/sunxi_display.c
similarity index 86%
rename from drivers/video/sunxi_display.c
rename to drivers/video/sunxi/sunxi_display.c
index 6f8ee01c10..9a4631eb77 100644
--- a/drivers/video/sunxi_display.c
+++ b/drivers/video/sunxi/sunxi_display.c
@@ -12,6 +12,7 @@ 
 #include <asm/arch/clock.h>
 #include <asm/arch/display.h>
 #include <asm/arch/gpio.h>
+#include <asm/arch/lcdc.h>
 #include <asm/arch/pwm.h>
 #include <asm/global_data.h>
 #include <asm/gpio.h>
@@ -23,10 +24,10 @@ 
 #include <i2c.h>
 #include <malloc.h>
 #include <video_fb.h>
-#include "videomodes.h"
-#include "anx9804.h"
-#include "hitachi_tx18d42vm_lcd.h"
-#include "ssd2828.h"
+#include "../videomodes.h"
+#include "../anx9804.h"
+#include "../hitachi_tx18d42vm_lcd.h"
+#include "../ssd2828.h"
 
 #ifdef CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW
 #define PWM_ON 0
@@ -630,8 +631,6 @@  static void sunxi_lcdc_init(void)
 {
 	struct sunxi_ccm_reg * const ccm =
 		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
-	struct sunxi_lcdc_reg * const lcdc =
-		(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
 
 	/* Reset off */
 #ifdef CONFIG_SUNXI_GEN_SUN6I
@@ -650,45 +649,7 @@  static void sunxi_lcdc_init(void)
 #endif
 #endif
 
-	/* Init lcdc */
-	writel(0, &lcdc->ctrl); /* Disable tcon */
-	writel(0, &lcdc->int0); /* Disable all interrupts */
-
-	/* Disable tcon0 dot clock */
-	clrbits_le32(&lcdc->tcon0_dclk, SUNXI_LCDC_TCON0_DCLK_ENABLE);
-
-	/* Set all io lines to tristate */
-	writel(0xffffffff, &lcdc->tcon0_io_tristate);
-	writel(0xffffffff, &lcdc->tcon1_io_tristate);
-}
-
-static void sunxi_lcdc_enable(void)
-{
-	struct sunxi_lcdc_reg * const lcdc =
-		(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);
-#ifdef CONFIG_SUNXI_GEN_SUN6I
-	udelay(2); /* delay at least 1200 ns */
-	setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_EN_MB);
-	udelay(2); /* delay at least 1200 ns */
-	setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVC);
-	if (sunxi_display.depth == 18)
-		setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0x7));
-	else
-		setbits_le32(&lcdc->lvds_ana0, SUNXI_LCDC_LVDS_ANA0_DRVD(0xf));
-#else
-	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
-#endif
+	lcdc_init((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE);
 }
 
 static void sunxi_lcdc_panel_enable(void)
@@ -758,17 +719,31 @@  static void sunxi_lcdc_backlight_enable(void)
 		gpio_direction_output(pin, PWM_ON);
 }
 
-static int sunxi_lcdc_get_clk_delay(const struct ctfb_res_modes *mode, int tcon)
+static void sunxi_ctfb_mode_to_display_timing(const struct ctfb_res_modes *mode,
+					      struct display_timing *timing)
 {
-	int delay;
+	timing->pixelclock.typ = mode->pixclock_khz * 1000;
 
-	delay = mode->lower_margin + mode->vsync_len + mode->upper_margin;
-	if (mode->vmode == FB_VMODE_INTERLACED)
-		delay /= 2;
-	if (tcon == 1)
-		delay -= 2;
+	timing->hactive.typ = mode->xres;
+	timing->hfront_porch.typ = mode->right_margin;
+	timing->hback_porch.typ = mode->left_margin;
+	timing->hsync_len.typ = mode->hsync_len;
+
+	timing->vactive.typ = mode->yres;
+	timing->vfront_porch.typ = mode->lower_margin;
+	timing->vback_porch.typ = mode->upper_margin;
+	timing->vsync_len.typ = mode->vsync_len;
 
-	return (delay > 30) ? 30 : delay;
+	if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
+		timing->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
+	else
+		timing->flags |= DISPLAY_FLAGS_HSYNC_LOW;
+	if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+		timing->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
+	else
+		timing->flags |= DISPLAY_FLAGS_VSYNC_LOW;
+	if (mode->vmode == FB_VMODE_INTERLACED)
+		timing->flags |= DISPLAY_FLAGS_INTERLACED;
 }
 
 static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
@@ -776,7 +751,8 @@  static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
 {
 	struct sunxi_lcdc_reg * const lcdc =
 		(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
-	int bp, clk_delay, clk_div, clk_double, pin, total, val;
+	int clk_div, clk_double, pin;
+	struct display_timing timing;
 
 #if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS
 	for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) {
@@ -796,73 +772,9 @@  static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
 
 	sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double);
 
-	/* Use tcon0 */
-	clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
-			SUNXI_LCDC_CTRL_IO_MAP_TCON0);
-
-	clk_delay = sunxi_lcdc_get_clk_delay(mode, 0);
-	writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
-	       SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
-
-	writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
-	       SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
-
-	writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
-	       &lcdc->tcon0_timing_active);
-
-	bp = mode->hsync_len + mode->left_margin;
-	total = mode->xres + mode->right_margin + bp;
-	writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
-	       SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
-
-	bp = mode->vsync_len + mode->upper_margin;
-	total = mode->yres + mode->lower_margin + bp;
-	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);
-
-	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) |
-	       SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0, &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]);
-		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[1]);
-		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[2]);
-		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[3]);
-		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[4]);
-		writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
-		writel(SUNXI_LCDC_TCON0_FRM_TAB0, &lcdc->tcon0_frm_table[0]);
-		writel(SUNXI_LCDC_TCON0_FRM_TAB1, &lcdc->tcon0_frm_table[1]);
-		writel(SUNXI_LCDC_TCON0_FRM_TAB2, &lcdc->tcon0_frm_table[2]);
-		writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
-		writel(((sunxi_display.depth == 18) ?
-			SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
-			SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
-		       &lcdc->tcon0_frm_ctrl);
-	}
-
-	val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(CONFIG_VIDEO_LCD_DCLK_PHASE);
-	if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
-		val |= SUNXI_LCDC_TCON_HSYNC_MASK;
-	if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
-		val |= SUNXI_LCDC_TCON_VSYNC_MASK;
-
-#ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH
-	if (for_ext_vga_dac)
-		val = 0;
-#endif
-	writel(val, &lcdc->tcon0_io_polarity);
-
-	writel(0, &lcdc->tcon0_io_tristate);
+	sunxi_ctfb_mode_to_display_timing(mode, &timing);
+	lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
+			    sunxi_display.depth, CONFIG_VIDEO_LCD_DCLK_PHASE);
 }
 
 #if defined CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || defined CONFIG_VIDEO_COMPOSITE
@@ -872,65 +784,17 @@  static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode,
 {
 	struct sunxi_lcdc_reg * const lcdc =
 		(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
-	int bp, clk_delay, total, val, yres;
-
-	/* Use tcon1 */
-	clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
-			SUNXI_LCDC_CTRL_IO_MAP_TCON1);
-
-	clk_delay = sunxi_lcdc_get_clk_delay(mode, 1);
-	writel(SUNXI_LCDC_TCON1_CTRL_ENABLE |
-	       ((mode->vmode == FB_VMODE_INTERLACED) ?
-			SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE : 0) |
-	       SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon1_ctrl);
+	struct display_timing timing;
 
-	yres = mode->yres;
-	if (mode->vmode == FB_VMODE_INTERLACED)
-		yres /= 2;
-	writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
-	       &lcdc->tcon1_timing_source);
-	writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
-	       &lcdc->tcon1_timing_scale);
-	writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(yres),
-	       &lcdc->tcon1_timing_out);
-
-	bp = mode->hsync_len + mode->left_margin;
-	total = mode->xres + mode->right_margin + bp;
-	writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total) |
-	       SUNXI_LCDC_TCON1_TIMING_H_BP(bp), &lcdc->tcon1_timing_h);
-
-	bp = mode->vsync_len + mode->upper_margin;
-	total = mode->yres + mode->lower_margin + bp;
-	if (mode->vmode == FB_VMODE_NONINTERLACED)
-		total *= 2;
-	writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total) |
-	       SUNXI_LCDC_TCON1_TIMING_V_BP(bp), &lcdc->tcon1_timing_v);
-
-	writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
-	       &lcdc->tcon1_timing_sync);
+	sunxi_ctfb_mode_to_display_timing(mode, &timing);
+	lcdc_tcon1_mode_set(lcdc, &timing, use_portd_hvsync,
+			    sunxi_is_composite());
 
 	if (use_portd_hvsync) {
 		sunxi_gpio_set_cfgpin(SUNXI_GPD(26), SUNXI_GPD_LCD0);
 		sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0);
-
-		val = 0;
-		if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
-			val |= SUNXI_LCDC_TCON_HSYNC_MASK;
-		if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
-			val |= SUNXI_LCDC_TCON_VSYNC_MASK;
-		writel(val, &lcdc->tcon1_io_polarity);
-
-		clrbits_le32(&lcdc->tcon1_io_tristate,
-			     SUNXI_LCDC_TCON_VSYNC_MASK |
-			     SUNXI_LCDC_TCON_HSYNC_MASK);
 	}
 
-#ifdef CONFIG_MACH_SUN5I
-	if (sunxi_is_composite())
-		clrsetbits_le32(&lcdc->mux_ctrl, SUNXI_LCDC_MUX_CTRL_SRC0_MASK,
-				SUNXI_LCDC_MUX_CTRL_SRC0(1));
-#endif
-
 	sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double);
 }
 #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
@@ -1223,7 +1087,7 @@  static void sunxi_mode_set(const struct ctfb_res_modes *mode,
 		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
 		sunxi_hdmi_mode_set(mode, clk_div, clk_double);
 		sunxi_composer_enable();
-		sunxi_lcdc_enable();
+		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE, 0);
 		sunxi_hdmi_enable();
 #endif
 		break;
@@ -1253,7 +1117,8 @@  static void sunxi_mode_set(const struct ctfb_res_modes *mode,
 		sunxi_composer_mode_set(mode, address);
 		sunxi_lcdc_tcon0_mode_set(mode, false);
 		sunxi_composer_enable();
-		sunxi_lcdc_enable();
+		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE,
+			    sunxi_display.depth);
 #ifdef CONFIG_VIDEO_LCD_SSD2828
 		sunxi_ssd2828_init(mode);
 #endif
@@ -1265,13 +1130,14 @@  static void sunxi_mode_set(const struct ctfb_res_modes *mode,
 		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 1);
 		sunxi_tvencoder_mode_set();
 		sunxi_composer_enable();
-		sunxi_lcdc_enable();
+		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE, 0);
 		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();
+		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE,
+			    sunxi_display.depth);
+		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE, 0);
 		sunxi_vga_external_dac_enable();
 #endif
 		break;
@@ -1284,7 +1150,7 @@  static void sunxi_mode_set(const struct ctfb_res_modes *mode,
 		sunxi_lcdc_tcon1_mode_set(mode, &clk_div, &clk_double, 0);
 		sunxi_tvencoder_mode_set();
 		sunxi_composer_enable();
-		sunxi_lcdc_enable();
+		lcdc_enable((struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE, 0);
 		sunxi_tvencoder_enable();
 #endif
 		break;