xref: /rk3399_rockchip-uboot/drivers/video/atmel_hlcdfb.c (revision b491b49882fc71838b46c47a860daf2978c80be4)
1f6b690e6SBo Shen /*
2f6b690e6SBo Shen  * Driver for AT91/AT32 MULTI LAYER LCD Controller
3f6b690e6SBo Shen  *
4f6b690e6SBo Shen  * Copyright (C) 2012 Atmel Corporation
5f6b690e6SBo Shen  *
61a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
7f6b690e6SBo Shen  */
8f6b690e6SBo Shen 
9f6b690e6SBo Shen #include <common.h>
10f6b690e6SBo Shen #include <asm/io.h>
11f6b690e6SBo Shen #include <asm/arch/gpio.h>
12f6b690e6SBo Shen #include <asm/arch/clk.h>
137927831eSSongjun Wu #include <clk.h>
147927831eSSongjun Wu #include <dm.h>
157927831eSSongjun Wu #include <fdtdec.h>
16f6b690e6SBo Shen #include <lcd.h>
177927831eSSongjun Wu #include <video.h>
187927831eSSongjun Wu #include <wait_bit.h>
19f6b690e6SBo Shen #include <atmel_hlcdc.h>
20f6b690e6SBo Shen 
2138b55087SNikita Kiryanov #if defined(CONFIG_LCD_LOGO)
2238b55087SNikita Kiryanov #include <bmp_logo.h>
2338b55087SNikita Kiryanov #endif
2438b55087SNikita Kiryanov 
257927831eSSongjun Wu DECLARE_GLOBAL_DATA_PTR;
267927831eSSongjun Wu 
277927831eSSongjun Wu #ifndef CONFIG_DM_VIDEO
287927831eSSongjun Wu 
29f6b690e6SBo Shen /* configurable parameters */
30f6b690e6SBo Shen #define ATMEL_LCDC_CVAL_DEFAULT		0xc8
31f6b690e6SBo Shen #define ATMEL_LCDC_DMA_BURST_LEN	8
32f6b690e6SBo Shen #ifndef ATMEL_LCDC_GUARD_TIME
33f6b690e6SBo Shen #define ATMEL_LCDC_GUARD_TIME		1
34f6b690e6SBo Shen #endif
35f6b690e6SBo Shen 
36f6b690e6SBo Shen #define ATMEL_LCDC_FIFO_SIZE		512
37f6b690e6SBo Shen 
38cfcd1c03SBo Shen /*
39cfcd1c03SBo Shen  * the CLUT register map as following
40cfcd1c03SBo Shen  * RCLUT(24 ~ 16), GCLUT(15 ~ 8), BCLUT(7 ~ 0)
41cfcd1c03SBo Shen  */
lcd_setcolreg(ushort regno,ushort red,ushort green,ushort blue)42cfcd1c03SBo Shen void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
43cfcd1c03SBo Shen {
447927831eSSongjun Wu 	writel(panel_info.mmio + ATMEL_LCDC_LUT(regno),
457927831eSSongjun Wu 	       ((red << LCDC_BASECLUT_RCLUT_Pos) & LCDC_BASECLUT_RCLUT_Msk)
46cfcd1c03SBo Shen 	       | ((green << LCDC_BASECLUT_GCLUT_Pos) & LCDC_BASECLUT_GCLUT_Msk)
477927831eSSongjun Wu 	       | ((blue << LCDC_BASECLUT_BCLUT_Pos) & LCDC_BASECLUT_BCLUT_Msk));
48cfcd1c03SBo Shen }
49cfcd1c03SBo Shen 
configuration_get_cmap(void)5038b55087SNikita Kiryanov ushort *configuration_get_cmap(void)
5138b55087SNikita Kiryanov {
5238b55087SNikita Kiryanov #if defined(CONFIG_LCD_LOGO)
5338b55087SNikita Kiryanov 	return bmp_logo_palette;
5438b55087SNikita Kiryanov #else
5538b55087SNikita Kiryanov 	return NULL;
5638b55087SNikita Kiryanov #endif
5738b55087SNikita Kiryanov }
5838b55087SNikita Kiryanov 
lcd_ctrl_init(void * lcdbase)59f6b690e6SBo Shen void lcd_ctrl_init(void *lcdbase)
60f6b690e6SBo Shen {
61f6b690e6SBo Shen 	unsigned long value;
62f6b690e6SBo Shen 	struct lcd_dma_desc *desc;
63f6b690e6SBo Shen 	struct atmel_hlcd_regs *regs;
647927831eSSongjun Wu 	int ret;
65f6b690e6SBo Shen 
66f6b690e6SBo Shen 	if (!has_lcdc())
67f6b690e6SBo Shen 		return;     /* No lcdc */
68f6b690e6SBo Shen 
69f6b690e6SBo Shen 	regs = (struct atmel_hlcd_regs *)panel_info.mmio;
70f6b690e6SBo Shen 
71f6b690e6SBo Shen 	/* Disable DISP signal */
727927831eSSongjun Wu 	writel(LCDC_LCDDIS_DISPDIS, &regs->lcdc_lcddis);
73*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
747927831eSSongjun Wu 				false, 1000, false);
757927831eSSongjun Wu 	if (ret)
767927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
77f6b690e6SBo Shen 	/* Disable synchronization */
787927831eSSongjun Wu 	writel(LCDC_LCDDIS_SYNCDIS, &regs->lcdc_lcddis);
79*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
807927831eSSongjun Wu 				false, 1000, false);
817927831eSSongjun Wu 	if (ret)
827927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
83f6b690e6SBo Shen 	/* Disable pixel clock */
847927831eSSongjun Wu 	writel(LCDC_LCDDIS_CLKDIS, &regs->lcdc_lcddis);
85*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
867927831eSSongjun Wu 				false, 1000, false);
877927831eSSongjun Wu 	if (ret)
887927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
89f6b690e6SBo Shen 	/* Disable PWM */
907927831eSSongjun Wu 	writel(LCDC_LCDDIS_PWMDIS, &regs->lcdc_lcddis);
91*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
927927831eSSongjun Wu 				false, 1000, false);
937927831eSSongjun Wu 	if (ret)
947927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
95f6b690e6SBo Shen 
96f6b690e6SBo Shen 	/* Set pixel clock */
97f6b690e6SBo Shen 	value = get_lcdc_clk_rate(0) / panel_info.vl_clk;
98f6b690e6SBo Shen 	if (get_lcdc_clk_rate(0) % panel_info.vl_clk)
99f6b690e6SBo Shen 		value++;
100f6b690e6SBo Shen 
101f6b690e6SBo Shen 	if (value < 1) {
102f6b690e6SBo Shen 		/* Using system clock as pixel clock */
1037927831eSSongjun Wu 		writel(LCDC_LCDCFG0_CLKDIV(0)
104f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISHCR
105f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISHEO
106f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISOVR1
107f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISBASE
108f6b690e6SBo Shen 			| panel_info.vl_clk_pol
1097927831eSSongjun Wu 			| LCDC_LCDCFG0_CLKSEL,
1107927831eSSongjun Wu 			&regs->lcdc_lcdcfg0);
111f6b690e6SBo Shen 
112f6b690e6SBo Shen 	} else {
1137927831eSSongjun Wu 		writel(LCDC_LCDCFG0_CLKDIV(value - 2)
114f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISHCR
115f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISHEO
116f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISOVR1
117f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISBASE
1187927831eSSongjun Wu 			| panel_info.vl_clk_pol,
1197927831eSSongjun Wu 			&regs->lcdc_lcdcfg0);
120f6b690e6SBo Shen 	}
121f6b690e6SBo Shen 
122f6b690e6SBo Shen 	/* Initialize control register 5 */
123f6b690e6SBo Shen 	value = 0;
124f6b690e6SBo Shen 
125f6b690e6SBo Shen 	value |= panel_info.vl_sync;
126f6b690e6SBo Shen 
127f6b690e6SBo Shen #ifndef LCD_OUTPUT_BPP
128f6b690e6SBo Shen 	/* Output is 24bpp */
129f6b690e6SBo Shen 	value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
130f6b690e6SBo Shen #else
131f6b690e6SBo Shen 	switch (LCD_OUTPUT_BPP) {
132f6b690e6SBo Shen 	case 12:
133f6b690e6SBo Shen 		value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP;
134f6b690e6SBo Shen 		break;
135f6b690e6SBo Shen 	case 16:
136f6b690e6SBo Shen 		value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP;
137f6b690e6SBo Shen 		break;
138f6b690e6SBo Shen 	case 18:
139f6b690e6SBo Shen 		value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP;
140f6b690e6SBo Shen 		break;
141f6b690e6SBo Shen 	case 24:
142f6b690e6SBo Shen 		value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
143f6b690e6SBo Shen 		break;
144f6b690e6SBo Shen 	default:
145f6b690e6SBo Shen 		BUG();
146f6b690e6SBo Shen 		break;
147f6b690e6SBo Shen 	}
148f6b690e6SBo Shen #endif
149f6b690e6SBo Shen 
150f6b690e6SBo Shen 	value |= LCDC_LCDCFG5_GUARDTIME(ATMEL_LCDC_GUARD_TIME);
151f6b690e6SBo Shen 	value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS);
1527927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg5);
153f6b690e6SBo Shen 
154f6b690e6SBo Shen 	/* Vertical & Horizontal Timing */
155f6b690e6SBo Shen 	value = LCDC_LCDCFG1_VSPW(panel_info.vl_vsync_len - 1);
156f6b690e6SBo Shen 	value |= LCDC_LCDCFG1_HSPW(panel_info.vl_hsync_len - 1);
1577927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg1);
158f6b690e6SBo Shen 
1591161f98dSWu, Josh 	value = LCDC_LCDCFG2_VBPW(panel_info.vl_upper_margin);
1601161f98dSWu, Josh 	value |= LCDC_LCDCFG2_VFPW(panel_info.vl_lower_margin - 1);
1617927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg2);
162f6b690e6SBo Shen 
1631161f98dSWu, Josh 	value = LCDC_LCDCFG3_HBPW(panel_info.vl_left_margin - 1);
1641161f98dSWu, Josh 	value |= LCDC_LCDCFG3_HFPW(panel_info.vl_right_margin - 1);
1657927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg3);
166f6b690e6SBo Shen 
167f6b690e6SBo Shen 	/* Display size */
168f6b690e6SBo Shen 	value = LCDC_LCDCFG4_RPF(panel_info.vl_row - 1);
169f6b690e6SBo Shen 	value |= LCDC_LCDCFG4_PPL(panel_info.vl_col - 1);
1707927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg4);
171f6b690e6SBo Shen 
1727927831eSSongjun Wu 	writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
1737927831eSSongjun Wu 	       &regs->lcdc_basecfg0);
174f6b690e6SBo Shen 
175f6b690e6SBo Shen 	switch (NBITS(panel_info.vl_bpix)) {
176f6b690e6SBo Shen 	case 16:
1777927831eSSongjun Wu 		writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565,
1787927831eSSongjun Wu 		       &regs->lcdc_basecfg1);
179f6b690e6SBo Shen 		break;
1808c1b7172SMarek Vasut 	case 32:
1817927831eSSongjun Wu 		writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
1827927831eSSongjun Wu 		       &regs->lcdc_basecfg1);
1838c1b7172SMarek Vasut 		break;
184f6b690e6SBo Shen 	default:
185f6b690e6SBo Shen 		BUG();
186f6b690e6SBo Shen 		break;
187f6b690e6SBo Shen 	}
188f6b690e6SBo Shen 
1897927831eSSongjun Wu 	writel(LCDC_BASECFG2_XSTRIDE(0), &regs->lcdc_basecfg2);
1907927831eSSongjun Wu 	writel(0, &regs->lcdc_basecfg3);
1917927831eSSongjun Wu 	writel(LCDC_BASECFG4_DMA, &regs->lcdc_basecfg4);
192f6b690e6SBo Shen 
193f6b690e6SBo Shen 	/* Disable all interrupts */
1947927831eSSongjun Wu 	writel(~0UL, &regs->lcdc_lcdidr);
1957927831eSSongjun Wu 	writel(~0UL, &regs->lcdc_baseidr);
196f6b690e6SBo Shen 
197f6b690e6SBo Shen 	/* Setup the DMA descriptor, this descriptor will loop to itself */
198f6b690e6SBo Shen 	desc = (struct lcd_dma_desc *)(lcdbase - 16);
199f6b690e6SBo Shen 
200f6b690e6SBo Shen 	desc->address = (u32)lcdbase;
201f6b690e6SBo Shen 	/* Disable DMA transfer interrupt & descriptor loaded interrupt. */
202f6b690e6SBo Shen 	desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN
203f6b690e6SBo Shen 			| LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH;
204f6b690e6SBo Shen 	desc->next = (u32)desc;
205f6b690e6SBo Shen 
206b137bd8cSWu, Josh 	/* Flush the DMA descriptor if we enabled dcache */
207b137bd8cSWu, Josh 	flush_dcache_range((u32)desc, (u32)desc + sizeof(*desc));
208b137bd8cSWu, Josh 
2097927831eSSongjun Wu 	writel(desc->address, &regs->lcdc_baseaddr);
2107927831eSSongjun Wu 	writel(desc->control, &regs->lcdc_basectrl);
2117927831eSSongjun Wu 	writel(desc->next, &regs->lcdc_basenext);
2127927831eSSongjun Wu 	writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
2137927831eSSongjun Wu 	       &regs->lcdc_basecher);
214f6b690e6SBo Shen 
215f6b690e6SBo Shen 	/* Enable LCD */
2167927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
2177927831eSSongjun Wu 	writel(value | LCDC_LCDEN_CLKEN, &regs->lcdc_lcden);
218*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
2197927831eSSongjun Wu 				true, 1000, false);
2207927831eSSongjun Wu 	if (ret)
2217927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
2227927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
2237927831eSSongjun Wu 	writel(value | LCDC_LCDEN_SYNCEN, &regs->lcdc_lcden);
224*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
2257927831eSSongjun Wu 				true, 1000, false);
2267927831eSSongjun Wu 	if (ret)
2277927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
2287927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
2297927831eSSongjun Wu 	writel(value | LCDC_LCDEN_DISPEN, &regs->lcdc_lcden);
230*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
2317927831eSSongjun Wu 				true, 1000, false);
2327927831eSSongjun Wu 	if (ret)
2337927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
2347927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
2357927831eSSongjun Wu 	writel(value | LCDC_LCDEN_PWMEN, &regs->lcdc_lcden);
236*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
2377927831eSSongjun Wu 				true, 1000, false);
2387927831eSSongjun Wu 	if (ret)
2397927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
240b137bd8cSWu, Josh 
241b137bd8cSWu, Josh 	/* Enable flushing if we enabled dcache */
242b137bd8cSWu, Josh 	lcd_set_flush_dcache(1);
243f6b690e6SBo Shen }
2447927831eSSongjun Wu 
2457927831eSSongjun Wu #else
2467927831eSSongjun Wu 
2477927831eSSongjun Wu enum {
2487927831eSSongjun Wu 	LCD_MAX_WIDTH		= 1024,
2497927831eSSongjun Wu 	LCD_MAX_HEIGHT		= 768,
2507927831eSSongjun Wu 	LCD_MAX_LOG2_BPP	= VIDEO_BPP16,
2517927831eSSongjun Wu };
2527927831eSSongjun Wu 
2537927831eSSongjun Wu struct atmel_hlcdc_priv {
2547927831eSSongjun Wu 	struct atmel_hlcd_regs *regs;
2557927831eSSongjun Wu 	struct display_timing timing;
2567927831eSSongjun Wu 	unsigned int vl_bpix;
2577927831eSSongjun Wu 	unsigned int output_mode;
2587927831eSSongjun Wu 	unsigned int guard_time;
2597927831eSSongjun Wu 	ulong clk_rate;
2607927831eSSongjun Wu };
2617927831eSSongjun Wu 
at91_hlcdc_enable_clk(struct udevice * dev)2627927831eSSongjun Wu static int at91_hlcdc_enable_clk(struct udevice *dev)
2637927831eSSongjun Wu {
2647927831eSSongjun Wu 	struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
2657927831eSSongjun Wu 	struct clk clk;
2667927831eSSongjun Wu 	ulong clk_rate;
2677927831eSSongjun Wu 	int ret;
2687927831eSSongjun Wu 
2697927831eSSongjun Wu 	ret = clk_get_by_index(dev, 0, &clk);
2707927831eSSongjun Wu 	if (ret)
2717927831eSSongjun Wu 		return -EINVAL;
2727927831eSSongjun Wu 
2737927831eSSongjun Wu 	ret = clk_enable(&clk);
2747927831eSSongjun Wu 	if (ret)
2757927831eSSongjun Wu 		return ret;
2767927831eSSongjun Wu 
2777927831eSSongjun Wu 	clk_rate = clk_get_rate(&clk);
2787927831eSSongjun Wu 	if (!clk_rate) {
2797927831eSSongjun Wu 		clk_disable(&clk);
2807927831eSSongjun Wu 		return -ENODEV;
2817927831eSSongjun Wu 	}
2827927831eSSongjun Wu 
2837927831eSSongjun Wu 	priv->clk_rate = clk_rate;
2847927831eSSongjun Wu 
2857927831eSSongjun Wu 	clk_free(&clk);
2867927831eSSongjun Wu 
2877927831eSSongjun Wu 	return 0;
2887927831eSSongjun Wu }
2897927831eSSongjun Wu 
atmel_hlcdc_init(struct udevice * dev)2907927831eSSongjun Wu static void atmel_hlcdc_init(struct udevice *dev)
2917927831eSSongjun Wu {
2927927831eSSongjun Wu 	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
2937927831eSSongjun Wu 	struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
2947927831eSSongjun Wu 	struct atmel_hlcd_regs *regs = priv->regs;
2957927831eSSongjun Wu 	struct display_timing *timing = &priv->timing;
2967927831eSSongjun Wu 	struct lcd_dma_desc *desc;
2977927831eSSongjun Wu 	unsigned long value, vl_clk_pol;
2987927831eSSongjun Wu 	int ret;
2997927831eSSongjun Wu 
3007927831eSSongjun Wu 	/* Disable DISP signal */
3017927831eSSongjun Wu 	writel(LCDC_LCDDIS_DISPDIS, &regs->lcdc_lcddis);
302*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
3037927831eSSongjun Wu 				false, 1000, false);
3047927831eSSongjun Wu 	if (ret)
3057927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
3067927831eSSongjun Wu 	/* Disable synchronization */
3077927831eSSongjun Wu 	writel(LCDC_LCDDIS_SYNCDIS, &regs->lcdc_lcddis);
308*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
3097927831eSSongjun Wu 				false, 1000, false);
3107927831eSSongjun Wu 	if (ret)
3117927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
3127927831eSSongjun Wu 	/* Disable pixel clock */
3137927831eSSongjun Wu 	writel(LCDC_LCDDIS_CLKDIS, &regs->lcdc_lcddis);
314*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
3157927831eSSongjun Wu 				false, 1000, false);
3167927831eSSongjun Wu 	if (ret)
3177927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
3187927831eSSongjun Wu 	/* Disable PWM */
3197927831eSSongjun Wu 	writel(LCDC_LCDDIS_PWMDIS, &regs->lcdc_lcddis);
320*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
3217927831eSSongjun Wu 				false, 1000, false);
3227927831eSSongjun Wu 	if (ret)
3237927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
3247927831eSSongjun Wu 
3257927831eSSongjun Wu 	/* Set pixel clock */
3267927831eSSongjun Wu 	value = priv->clk_rate / timing->pixelclock.typ;
3277927831eSSongjun Wu 	if (priv->clk_rate % timing->pixelclock.typ)
3287927831eSSongjun Wu 		value++;
3297927831eSSongjun Wu 
3307927831eSSongjun Wu 	vl_clk_pol = 0;
3317927831eSSongjun Wu 	if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
3327927831eSSongjun Wu 		vl_clk_pol = LCDC_LCDCFG0_CLKPOL;
3337927831eSSongjun Wu 
3347927831eSSongjun Wu 	if (value < 1) {
3357927831eSSongjun Wu 		/* Using system clock as pixel clock */
3367927831eSSongjun Wu 		writel(LCDC_LCDCFG0_CLKDIV(0)
3377927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISHCR
3387927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISHEO
3397927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISOVR1
3407927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISBASE
3417927831eSSongjun Wu 			| vl_clk_pol
3427927831eSSongjun Wu 			| LCDC_LCDCFG0_CLKSEL,
3437927831eSSongjun Wu 			&regs->lcdc_lcdcfg0);
3447927831eSSongjun Wu 
3457927831eSSongjun Wu 	} else {
3467927831eSSongjun Wu 		writel(LCDC_LCDCFG0_CLKDIV(value - 2)
3477927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISHCR
3487927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISHEO
3497927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISOVR1
3507927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISBASE
3517927831eSSongjun Wu 			| vl_clk_pol,
3527927831eSSongjun Wu 			&regs->lcdc_lcdcfg0);
3537927831eSSongjun Wu 	}
3547927831eSSongjun Wu 
3557927831eSSongjun Wu 	/* Initialize control register 5 */
3567927831eSSongjun Wu 	value = 0;
3577927831eSSongjun Wu 
3587927831eSSongjun Wu 	if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
3597927831eSSongjun Wu 		value |= LCDC_LCDCFG5_HSPOL;
3607927831eSSongjun Wu 	if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
3617927831eSSongjun Wu 		value |= LCDC_LCDCFG5_VSPOL;
3627927831eSSongjun Wu 
3637927831eSSongjun Wu 	switch (priv->output_mode) {
3647927831eSSongjun Wu 	case 12:
3657927831eSSongjun Wu 		value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP;
3667927831eSSongjun Wu 		break;
3677927831eSSongjun Wu 	case 16:
3687927831eSSongjun Wu 		value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP;
3697927831eSSongjun Wu 		break;
3707927831eSSongjun Wu 	case 18:
3717927831eSSongjun Wu 		value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP;
3727927831eSSongjun Wu 		break;
3737927831eSSongjun Wu 	case 24:
3747927831eSSongjun Wu 		value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
3757927831eSSongjun Wu 		break;
3767927831eSSongjun Wu 	default:
3777927831eSSongjun Wu 		BUG();
3787927831eSSongjun Wu 		break;
3797927831eSSongjun Wu 	}
3807927831eSSongjun Wu 
3817927831eSSongjun Wu 	value |= LCDC_LCDCFG5_GUARDTIME(priv->guard_time);
3827927831eSSongjun Wu 	value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS);
3837927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg5);
3847927831eSSongjun Wu 
3857927831eSSongjun Wu 	/* Vertical & Horizontal Timing */
3867927831eSSongjun Wu 	value = LCDC_LCDCFG1_VSPW(timing->vsync_len.typ - 1);
3877927831eSSongjun Wu 	value |= LCDC_LCDCFG1_HSPW(timing->hsync_len.typ - 1);
3887927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg1);
3897927831eSSongjun Wu 
3907927831eSSongjun Wu 	value = LCDC_LCDCFG2_VBPW(timing->vback_porch.typ);
3917927831eSSongjun Wu 	value |= LCDC_LCDCFG2_VFPW(timing->vfront_porch.typ - 1);
3927927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg2);
3937927831eSSongjun Wu 
3947927831eSSongjun Wu 	value = LCDC_LCDCFG3_HBPW(timing->hback_porch.typ - 1);
3957927831eSSongjun Wu 	value |= LCDC_LCDCFG3_HFPW(timing->hfront_porch.typ - 1);
3967927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg3);
3977927831eSSongjun Wu 
3987927831eSSongjun Wu 	/* Display size */
3997927831eSSongjun Wu 	value = LCDC_LCDCFG4_RPF(timing->vactive.typ - 1);
4007927831eSSongjun Wu 	value |= LCDC_LCDCFG4_PPL(timing->hactive.typ - 1);
4017927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg4);
4027927831eSSongjun Wu 
4037927831eSSongjun Wu 	writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
4047927831eSSongjun Wu 	       &regs->lcdc_basecfg0);
4057927831eSSongjun Wu 
4067927831eSSongjun Wu 	switch (VNBITS(priv->vl_bpix)) {
4077927831eSSongjun Wu 	case 16:
4087927831eSSongjun Wu 		writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565,
4097927831eSSongjun Wu 		       &regs->lcdc_basecfg1);
4107927831eSSongjun Wu 		break;
4117927831eSSongjun Wu 	case 32:
4127927831eSSongjun Wu 		writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
4137927831eSSongjun Wu 		       &regs->lcdc_basecfg1);
4147927831eSSongjun Wu 		break;
4157927831eSSongjun Wu 	default:
4167927831eSSongjun Wu 		BUG();
4177927831eSSongjun Wu 		break;
4187927831eSSongjun Wu 	}
4197927831eSSongjun Wu 
4207927831eSSongjun Wu 	writel(LCDC_BASECFG2_XSTRIDE(0), &regs->lcdc_basecfg2);
4217927831eSSongjun Wu 	writel(0, &regs->lcdc_basecfg3);
4227927831eSSongjun Wu 	writel(LCDC_BASECFG4_DMA, &regs->lcdc_basecfg4);
4237927831eSSongjun Wu 
4247927831eSSongjun Wu 	/* Disable all interrupts */
4257927831eSSongjun Wu 	writel(~0UL, &regs->lcdc_lcdidr);
4267927831eSSongjun Wu 	writel(~0UL, &regs->lcdc_baseidr);
4277927831eSSongjun Wu 
4287927831eSSongjun Wu 	/* Setup the DMA descriptor, this descriptor will loop to itself */
42931e5c892SWenyou Yang 	desc = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*desc));
43031e5c892SWenyou Yang 	if (!desc)
43131e5c892SWenyou Yang 		return;
4327927831eSSongjun Wu 
4337927831eSSongjun Wu 	desc->address = (u32)uc_plat->base;
4347927831eSSongjun Wu 
4357927831eSSongjun Wu 	/* Disable DMA transfer interrupt & descriptor loaded interrupt. */
4367927831eSSongjun Wu 	desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN
4377927831eSSongjun Wu 			| LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH;
4387927831eSSongjun Wu 	desc->next = (u32)desc;
4397927831eSSongjun Wu 
4407927831eSSongjun Wu 	/* Flush the DMA descriptor if we enabled dcache */
44131e5c892SWenyou Yang 	flush_dcache_range((u32)desc,
44231e5c892SWenyou Yang 			   ALIGN(((u32)desc + sizeof(*desc)),
44331e5c892SWenyou Yang 			   CONFIG_SYS_CACHELINE_SIZE));
4447927831eSSongjun Wu 
4457927831eSSongjun Wu 	writel(desc->address, &regs->lcdc_baseaddr);
4467927831eSSongjun Wu 	writel(desc->control, &regs->lcdc_basectrl);
4477927831eSSongjun Wu 	writel(desc->next, &regs->lcdc_basenext);
4487927831eSSongjun Wu 	writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
4497927831eSSongjun Wu 	       &regs->lcdc_basecher);
4507927831eSSongjun Wu 
4517927831eSSongjun Wu 	/* Enable LCD */
4527927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
4537927831eSSongjun Wu 	writel(value | LCDC_LCDEN_CLKEN, &regs->lcdc_lcden);
454*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
4557927831eSSongjun Wu 				true, 1000, false);
4567927831eSSongjun Wu 	if (ret)
4577927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
4587927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
4597927831eSSongjun Wu 	writel(value | LCDC_LCDEN_SYNCEN, &regs->lcdc_lcden);
460*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
4617927831eSSongjun Wu 				true, 1000, false);
4627927831eSSongjun Wu 	if (ret)
4637927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
4647927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
4657927831eSSongjun Wu 	writel(value | LCDC_LCDEN_DISPEN, &regs->lcdc_lcden);
466*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
4677927831eSSongjun Wu 				true, 1000, false);
4687927831eSSongjun Wu 	if (ret)
4697927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
4707927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
4717927831eSSongjun Wu 	writel(value | LCDC_LCDEN_PWMEN, &regs->lcdc_lcden);
472*b491b498SJon Lin 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
4737927831eSSongjun Wu 				true, 1000, false);
4747927831eSSongjun Wu 	if (ret)
4757927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
4767927831eSSongjun Wu }
4777927831eSSongjun Wu 
atmel_hlcdc_probe(struct udevice * dev)4787927831eSSongjun Wu static int atmel_hlcdc_probe(struct udevice *dev)
4797927831eSSongjun Wu {
4807927831eSSongjun Wu 	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
4817927831eSSongjun Wu 	struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
4827927831eSSongjun Wu 	int ret;
4837927831eSSongjun Wu 
4847927831eSSongjun Wu 	ret = at91_hlcdc_enable_clk(dev);
4857927831eSSongjun Wu 	if (ret)
4867927831eSSongjun Wu 		return ret;
4877927831eSSongjun Wu 
4887927831eSSongjun Wu 	atmel_hlcdc_init(dev);
4897927831eSSongjun Wu 
4907927831eSSongjun Wu 	uc_priv->xsize = priv->timing.hactive.typ;
4917927831eSSongjun Wu 	uc_priv->ysize = priv->timing.vactive.typ;
4927927831eSSongjun Wu 	uc_priv->bpix = priv->vl_bpix;
4937927831eSSongjun Wu 
4947927831eSSongjun Wu 	/* Enable flushing if we enabled dcache */
4957927831eSSongjun Wu 	video_set_flush_dcache(dev, true);
4967927831eSSongjun Wu 
4977927831eSSongjun Wu 	return 0;
4987927831eSSongjun Wu }
4997927831eSSongjun Wu 
atmel_hlcdc_ofdata_to_platdata(struct udevice * dev)5007927831eSSongjun Wu static int atmel_hlcdc_ofdata_to_platdata(struct udevice *dev)
5017927831eSSongjun Wu {
5027927831eSSongjun Wu 	struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
5037927831eSSongjun Wu 	const void *blob = gd->fdt_blob;
504da409cccSSimon Glass 	int node = dev_of_offset(dev);
5057927831eSSongjun Wu 
506a821c4afSSimon Glass 	priv->regs = (struct atmel_hlcd_regs *)devfdt_get_addr(dev);
5077927831eSSongjun Wu 	if (!priv->regs) {
5087927831eSSongjun Wu 		debug("%s: No display controller address\n", __func__);
5097927831eSSongjun Wu 		return -EINVAL;
5107927831eSSongjun Wu 	}
5117927831eSSongjun Wu 
512da409cccSSimon Glass 	if (fdtdec_decode_display_timing(blob, dev_of_offset(dev),
5137927831eSSongjun Wu 					 0, &priv->timing)) {
5147927831eSSongjun Wu 		debug("%s: Failed to decode display timing\n", __func__);
5157927831eSSongjun Wu 		return -EINVAL;
5167927831eSSongjun Wu 	}
5177927831eSSongjun Wu 
5187927831eSSongjun Wu 	if (priv->timing.hactive.typ > LCD_MAX_WIDTH)
5197927831eSSongjun Wu 		priv->timing.hactive.typ = LCD_MAX_WIDTH;
5207927831eSSongjun Wu 
5217927831eSSongjun Wu 	if (priv->timing.vactive.typ > LCD_MAX_HEIGHT)
5227927831eSSongjun Wu 		priv->timing.vactive.typ = LCD_MAX_HEIGHT;
5237927831eSSongjun Wu 
5247927831eSSongjun Wu 	priv->vl_bpix = fdtdec_get_int(blob, node, "atmel,vl-bpix", 0);
5257927831eSSongjun Wu 	if (!priv->vl_bpix) {
5267927831eSSongjun Wu 		debug("%s: Failed to get bits per pixel\n", __func__);
5277927831eSSongjun Wu 		return -EINVAL;
5287927831eSSongjun Wu 	}
5297927831eSSongjun Wu 
5307927831eSSongjun Wu 	priv->output_mode = fdtdec_get_int(blob, node, "atmel,output-mode", 24);
5317927831eSSongjun Wu 	priv->guard_time = fdtdec_get_int(blob, node, "atmel,guard-time", 1);
5327927831eSSongjun Wu 
5337927831eSSongjun Wu 	return 0;
5347927831eSSongjun Wu }
5357927831eSSongjun Wu 
atmel_hlcdc_bind(struct udevice * dev)5367927831eSSongjun Wu static int atmel_hlcdc_bind(struct udevice *dev)
5377927831eSSongjun Wu {
5387927831eSSongjun Wu 	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
5397927831eSSongjun Wu 
5407927831eSSongjun Wu 	uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
5417927831eSSongjun Wu 				(1 << LCD_MAX_LOG2_BPP) / 8;
5427927831eSSongjun Wu 
5437927831eSSongjun Wu 	debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
5447927831eSSongjun Wu 
5457927831eSSongjun Wu 	return 0;
5467927831eSSongjun Wu }
5477927831eSSongjun Wu 
5487927831eSSongjun Wu static const struct udevice_id atmel_hlcdc_ids[] = {
5497927831eSSongjun Wu 	{ .compatible = "atmel,sama5d2-hlcdc" },
5507927831eSSongjun Wu 	{ .compatible = "atmel,at91sam9x5-hlcdc" },
5517927831eSSongjun Wu 	{ }
5527927831eSSongjun Wu };
5537927831eSSongjun Wu 
5547927831eSSongjun Wu U_BOOT_DRIVER(atmel_hlcdfb) = {
5557927831eSSongjun Wu 	.name	= "atmel_hlcdfb",
5567927831eSSongjun Wu 	.id	= UCLASS_VIDEO,
5577927831eSSongjun Wu 	.of_match = atmel_hlcdc_ids,
5587927831eSSongjun Wu 	.bind	= atmel_hlcdc_bind,
5597927831eSSongjun Wu 	.probe	= atmel_hlcdc_probe,
5607927831eSSongjun Wu 	.ofdata_to_platdata = atmel_hlcdc_ofdata_to_platdata,
5617927831eSSongjun Wu 	.priv_auto_alloc_size = sizeof(struct atmel_hlcdc_priv),
5627927831eSSongjun Wu };
5637927831eSSongjun Wu 
5647927831eSSongjun Wu #endif
565