xref: /openbmc/u-boot/drivers/video/atmel_hlcdfb.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2f6b690e6SBo Shen /*
3f6b690e6SBo Shen  * Driver for AT91/AT32 MULTI LAYER LCD Controller
4f6b690e6SBo Shen  *
5f6b690e6SBo Shen  * Copyright (C) 2012 Atmel Corporation
6f6b690e6SBo Shen  */
7f6b690e6SBo Shen 
8f6b690e6SBo Shen #include <common.h>
9f6b690e6SBo Shen #include <asm/io.h>
10f6b690e6SBo Shen #include <asm/arch/gpio.h>
11f6b690e6SBo Shen #include <asm/arch/clk.h>
127927831eSSongjun Wu #include <clk.h>
137927831eSSongjun Wu #include <dm.h>
147927831eSSongjun Wu #include <fdtdec.h>
15f6b690e6SBo Shen #include <lcd.h>
167927831eSSongjun Wu #include <video.h>
177927831eSSongjun Wu #include <wait_bit.h>
18f6b690e6SBo Shen #include <atmel_hlcdc.h>
19f6b690e6SBo Shen 
2038b55087SNikita Kiryanov #if defined(CONFIG_LCD_LOGO)
2138b55087SNikita Kiryanov #include <bmp_logo.h>
2238b55087SNikita Kiryanov #endif
2338b55087SNikita Kiryanov 
247927831eSSongjun Wu DECLARE_GLOBAL_DATA_PTR;
257927831eSSongjun Wu 
267927831eSSongjun Wu #ifndef CONFIG_DM_VIDEO
277927831eSSongjun Wu 
28f6b690e6SBo Shen /* configurable parameters */
29f6b690e6SBo Shen #define ATMEL_LCDC_CVAL_DEFAULT		0xc8
30f6b690e6SBo Shen #define ATMEL_LCDC_DMA_BURST_LEN	8
31f6b690e6SBo Shen #ifndef ATMEL_LCDC_GUARD_TIME
32f6b690e6SBo Shen #define ATMEL_LCDC_GUARD_TIME		1
33f6b690e6SBo Shen #endif
34f6b690e6SBo Shen 
35f6b690e6SBo Shen #define ATMEL_LCDC_FIFO_SIZE		512
36f6b690e6SBo Shen 
37cfcd1c03SBo Shen /*
38cfcd1c03SBo Shen  * the CLUT register map as following
39cfcd1c03SBo Shen  * RCLUT(24 ~ 16), GCLUT(15 ~ 8), BCLUT(7 ~ 0)
40cfcd1c03SBo Shen  */
lcd_setcolreg(ushort regno,ushort red,ushort green,ushort blue)41cfcd1c03SBo Shen void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
42cfcd1c03SBo Shen {
437927831eSSongjun Wu 	writel(panel_info.mmio + ATMEL_LCDC_LUT(regno),
447927831eSSongjun Wu 	       ((red << LCDC_BASECLUT_RCLUT_Pos) & LCDC_BASECLUT_RCLUT_Msk)
45cfcd1c03SBo Shen 	       | ((green << LCDC_BASECLUT_GCLUT_Pos) & LCDC_BASECLUT_GCLUT_Msk)
467927831eSSongjun Wu 	       | ((blue << LCDC_BASECLUT_BCLUT_Pos) & LCDC_BASECLUT_BCLUT_Msk));
47cfcd1c03SBo Shen }
48cfcd1c03SBo Shen 
configuration_get_cmap(void)4938b55087SNikita Kiryanov ushort *configuration_get_cmap(void)
5038b55087SNikita Kiryanov {
5138b55087SNikita Kiryanov #if defined(CONFIG_LCD_LOGO)
5238b55087SNikita Kiryanov 	return bmp_logo_palette;
5338b55087SNikita Kiryanov #else
5438b55087SNikita Kiryanov 	return NULL;
5538b55087SNikita Kiryanov #endif
5638b55087SNikita Kiryanov }
5738b55087SNikita Kiryanov 
lcd_ctrl_init(void * lcdbase)58f6b690e6SBo Shen void lcd_ctrl_init(void *lcdbase)
59f6b690e6SBo Shen {
60f6b690e6SBo Shen 	unsigned long value;
61f6b690e6SBo Shen 	struct lcd_dma_desc *desc;
62f6b690e6SBo Shen 	struct atmel_hlcd_regs *regs;
637927831eSSongjun Wu 	int ret;
64f6b690e6SBo Shen 
65f6b690e6SBo Shen 	if (!has_lcdc())
66f6b690e6SBo Shen 		return;     /* No lcdc */
67f6b690e6SBo Shen 
68f6b690e6SBo Shen 	regs = (struct atmel_hlcd_regs *)panel_info.mmio;
69f6b690e6SBo Shen 
70f6b690e6SBo Shen 	/* Disable DISP signal */
717927831eSSongjun Wu 	writel(LCDC_LCDDIS_DISPDIS, &regs->lcdc_lcddis);
7248263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
737927831eSSongjun Wu 				false, 1000, false);
747927831eSSongjun Wu 	if (ret)
757927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
76f6b690e6SBo Shen 	/* Disable synchronization */
777927831eSSongjun Wu 	writel(LCDC_LCDDIS_SYNCDIS, &regs->lcdc_lcddis);
7848263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
797927831eSSongjun Wu 				false, 1000, false);
807927831eSSongjun Wu 	if (ret)
817927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
82f6b690e6SBo Shen 	/* Disable pixel clock */
837927831eSSongjun Wu 	writel(LCDC_LCDDIS_CLKDIS, &regs->lcdc_lcddis);
8448263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
857927831eSSongjun Wu 				false, 1000, false);
867927831eSSongjun Wu 	if (ret)
877927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
88f6b690e6SBo Shen 	/* Disable PWM */
897927831eSSongjun Wu 	writel(LCDC_LCDDIS_PWMDIS, &regs->lcdc_lcddis);
9048263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
917927831eSSongjun Wu 				false, 1000, false);
927927831eSSongjun Wu 	if (ret)
937927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
94f6b690e6SBo Shen 
95f6b690e6SBo Shen 	/* Set pixel clock */
96f6b690e6SBo Shen 	value = get_lcdc_clk_rate(0) / panel_info.vl_clk;
97f6b690e6SBo Shen 	if (get_lcdc_clk_rate(0) % panel_info.vl_clk)
98f6b690e6SBo Shen 		value++;
99f6b690e6SBo Shen 
100f6b690e6SBo Shen 	if (value < 1) {
101f6b690e6SBo Shen 		/* Using system clock as pixel clock */
1027927831eSSongjun Wu 		writel(LCDC_LCDCFG0_CLKDIV(0)
103f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISHCR
104f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISHEO
105f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISOVR1
106f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISBASE
107f6b690e6SBo Shen 			| panel_info.vl_clk_pol
1087927831eSSongjun Wu 			| LCDC_LCDCFG0_CLKSEL,
1097927831eSSongjun Wu 			&regs->lcdc_lcdcfg0);
110f6b690e6SBo Shen 
111f6b690e6SBo Shen 	} else {
1127927831eSSongjun Wu 		writel(LCDC_LCDCFG0_CLKDIV(value - 2)
113f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISHCR
114f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISHEO
115f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISOVR1
116f6b690e6SBo Shen 			| LCDC_LCDCFG0_CGDISBASE
1177927831eSSongjun Wu 			| panel_info.vl_clk_pol,
1187927831eSSongjun Wu 			&regs->lcdc_lcdcfg0);
119f6b690e6SBo Shen 	}
120f6b690e6SBo Shen 
121f6b690e6SBo Shen 	/* Initialize control register 5 */
122f6b690e6SBo Shen 	value = 0;
123f6b690e6SBo Shen 
124f6b690e6SBo Shen 	value |= panel_info.vl_sync;
125f6b690e6SBo Shen 
126f6b690e6SBo Shen #ifndef LCD_OUTPUT_BPP
127f6b690e6SBo Shen 	/* Output is 24bpp */
128f6b690e6SBo Shen 	value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
129f6b690e6SBo Shen #else
130f6b690e6SBo Shen 	switch (LCD_OUTPUT_BPP) {
131f6b690e6SBo Shen 	case 12:
132f6b690e6SBo Shen 		value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP;
133f6b690e6SBo Shen 		break;
134f6b690e6SBo Shen 	case 16:
135f6b690e6SBo Shen 		value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP;
136f6b690e6SBo Shen 		break;
137f6b690e6SBo Shen 	case 18:
138f6b690e6SBo Shen 		value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP;
139f6b690e6SBo Shen 		break;
140f6b690e6SBo Shen 	case 24:
141f6b690e6SBo Shen 		value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
142f6b690e6SBo Shen 		break;
143f6b690e6SBo Shen 	default:
144f6b690e6SBo Shen 		BUG();
145f6b690e6SBo Shen 		break;
146f6b690e6SBo Shen 	}
147f6b690e6SBo Shen #endif
148f6b690e6SBo Shen 
149f6b690e6SBo Shen 	value |= LCDC_LCDCFG5_GUARDTIME(ATMEL_LCDC_GUARD_TIME);
150f6b690e6SBo Shen 	value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS);
1517927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg5);
152f6b690e6SBo Shen 
153f6b690e6SBo Shen 	/* Vertical & Horizontal Timing */
154f6b690e6SBo Shen 	value = LCDC_LCDCFG1_VSPW(panel_info.vl_vsync_len - 1);
155f6b690e6SBo Shen 	value |= LCDC_LCDCFG1_HSPW(panel_info.vl_hsync_len - 1);
1567927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg1);
157f6b690e6SBo Shen 
1581161f98dSWu, Josh 	value = LCDC_LCDCFG2_VBPW(panel_info.vl_upper_margin);
1591161f98dSWu, Josh 	value |= LCDC_LCDCFG2_VFPW(panel_info.vl_lower_margin - 1);
1607927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg2);
161f6b690e6SBo Shen 
1621161f98dSWu, Josh 	value = LCDC_LCDCFG3_HBPW(panel_info.vl_left_margin - 1);
1631161f98dSWu, Josh 	value |= LCDC_LCDCFG3_HFPW(panel_info.vl_right_margin - 1);
1647927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg3);
165f6b690e6SBo Shen 
166f6b690e6SBo Shen 	/* Display size */
167f6b690e6SBo Shen 	value = LCDC_LCDCFG4_RPF(panel_info.vl_row - 1);
168f6b690e6SBo Shen 	value |= LCDC_LCDCFG4_PPL(panel_info.vl_col - 1);
1697927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg4);
170f6b690e6SBo Shen 
1717927831eSSongjun Wu 	writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
1727927831eSSongjun Wu 	       &regs->lcdc_basecfg0);
173f6b690e6SBo Shen 
174f6b690e6SBo Shen 	switch (NBITS(panel_info.vl_bpix)) {
175f6b690e6SBo Shen 	case 16:
1767927831eSSongjun Wu 		writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565,
1777927831eSSongjun Wu 		       &regs->lcdc_basecfg1);
178f6b690e6SBo Shen 		break;
1798c1b7172SMarek Vasut 	case 32:
1807927831eSSongjun Wu 		writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
1817927831eSSongjun Wu 		       &regs->lcdc_basecfg1);
1828c1b7172SMarek Vasut 		break;
183f6b690e6SBo Shen 	default:
184f6b690e6SBo Shen 		BUG();
185f6b690e6SBo Shen 		break;
186f6b690e6SBo Shen 	}
187f6b690e6SBo Shen 
1887927831eSSongjun Wu 	writel(LCDC_BASECFG2_XSTRIDE(0), &regs->lcdc_basecfg2);
1897927831eSSongjun Wu 	writel(0, &regs->lcdc_basecfg3);
1907927831eSSongjun Wu 	writel(LCDC_BASECFG4_DMA, &regs->lcdc_basecfg4);
191f6b690e6SBo Shen 
192f6b690e6SBo Shen 	/* Disable all interrupts */
1937927831eSSongjun Wu 	writel(~0UL, &regs->lcdc_lcdidr);
1947927831eSSongjun Wu 	writel(~0UL, &regs->lcdc_baseidr);
195f6b690e6SBo Shen 
196f6b690e6SBo Shen 	/* Setup the DMA descriptor, this descriptor will loop to itself */
197f6b690e6SBo Shen 	desc = (struct lcd_dma_desc *)(lcdbase - 16);
198f6b690e6SBo Shen 
199f6b690e6SBo Shen 	desc->address = (u32)lcdbase;
200f6b690e6SBo Shen 	/* Disable DMA transfer interrupt & descriptor loaded interrupt. */
201f6b690e6SBo Shen 	desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN
202f6b690e6SBo Shen 			| LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH;
203f6b690e6SBo Shen 	desc->next = (u32)desc;
204f6b690e6SBo Shen 
205b137bd8cSWu, Josh 	/* Flush the DMA descriptor if we enabled dcache */
206b137bd8cSWu, Josh 	flush_dcache_range((u32)desc, (u32)desc + sizeof(*desc));
207b137bd8cSWu, Josh 
2087927831eSSongjun Wu 	writel(desc->address, &regs->lcdc_baseaddr);
2097927831eSSongjun Wu 	writel(desc->control, &regs->lcdc_basectrl);
2107927831eSSongjun Wu 	writel(desc->next, &regs->lcdc_basenext);
2117927831eSSongjun Wu 	writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
2127927831eSSongjun Wu 	       &regs->lcdc_basecher);
213f6b690e6SBo Shen 
214f6b690e6SBo Shen 	/* Enable LCD */
2157927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
2167927831eSSongjun Wu 	writel(value | LCDC_LCDEN_CLKEN, &regs->lcdc_lcden);
21748263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
2187927831eSSongjun Wu 				true, 1000, false);
2197927831eSSongjun Wu 	if (ret)
2207927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
2217927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
2227927831eSSongjun Wu 	writel(value | LCDC_LCDEN_SYNCEN, &regs->lcdc_lcden);
22348263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
2247927831eSSongjun Wu 				true, 1000, false);
2257927831eSSongjun Wu 	if (ret)
2267927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
2277927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
2287927831eSSongjun Wu 	writel(value | LCDC_LCDEN_DISPEN, &regs->lcdc_lcden);
22948263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
2307927831eSSongjun Wu 				true, 1000, false);
2317927831eSSongjun Wu 	if (ret)
2327927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
2337927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
2347927831eSSongjun Wu 	writel(value | LCDC_LCDEN_PWMEN, &regs->lcdc_lcden);
23548263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
2367927831eSSongjun Wu 				true, 1000, false);
2377927831eSSongjun Wu 	if (ret)
2387927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
239b137bd8cSWu, Josh 
240b137bd8cSWu, Josh 	/* Enable flushing if we enabled dcache */
241b137bd8cSWu, Josh 	lcd_set_flush_dcache(1);
242f6b690e6SBo Shen }
2437927831eSSongjun Wu 
2447927831eSSongjun Wu #else
2457927831eSSongjun Wu 
2467927831eSSongjun Wu enum {
2477927831eSSongjun Wu 	LCD_MAX_WIDTH		= 1024,
2487927831eSSongjun Wu 	LCD_MAX_HEIGHT		= 768,
2497927831eSSongjun Wu 	LCD_MAX_LOG2_BPP	= VIDEO_BPP16,
2507927831eSSongjun Wu };
2517927831eSSongjun Wu 
2527927831eSSongjun Wu struct atmel_hlcdc_priv {
2537927831eSSongjun Wu 	struct atmel_hlcd_regs *regs;
2547927831eSSongjun Wu 	struct display_timing timing;
2557927831eSSongjun Wu 	unsigned int vl_bpix;
2567927831eSSongjun Wu 	unsigned int output_mode;
2577927831eSSongjun Wu 	unsigned int guard_time;
2587927831eSSongjun Wu 	ulong clk_rate;
2597927831eSSongjun Wu };
2607927831eSSongjun Wu 
at91_hlcdc_enable_clk(struct udevice * dev)2617927831eSSongjun Wu static int at91_hlcdc_enable_clk(struct udevice *dev)
2627927831eSSongjun Wu {
2637927831eSSongjun Wu 	struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
2647927831eSSongjun Wu 	struct clk clk;
2657927831eSSongjun Wu 	ulong clk_rate;
2667927831eSSongjun Wu 	int ret;
2677927831eSSongjun Wu 
2687927831eSSongjun Wu 	ret = clk_get_by_index(dev, 0, &clk);
2697927831eSSongjun Wu 	if (ret)
2707927831eSSongjun Wu 		return -EINVAL;
2717927831eSSongjun Wu 
2727927831eSSongjun Wu 	ret = clk_enable(&clk);
2737927831eSSongjun Wu 	if (ret)
2747927831eSSongjun Wu 		return ret;
2757927831eSSongjun Wu 
2767927831eSSongjun Wu 	clk_rate = clk_get_rate(&clk);
2777927831eSSongjun Wu 	if (!clk_rate) {
2787927831eSSongjun Wu 		clk_disable(&clk);
2797927831eSSongjun Wu 		return -ENODEV;
2807927831eSSongjun Wu 	}
2817927831eSSongjun Wu 
2827927831eSSongjun Wu 	priv->clk_rate = clk_rate;
2837927831eSSongjun Wu 
2847927831eSSongjun Wu 	clk_free(&clk);
2857927831eSSongjun Wu 
2867927831eSSongjun Wu 	return 0;
2877927831eSSongjun Wu }
2887927831eSSongjun Wu 
atmel_hlcdc_init(struct udevice * dev)2897927831eSSongjun Wu static void atmel_hlcdc_init(struct udevice *dev)
2907927831eSSongjun Wu {
2917927831eSSongjun Wu 	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
2927927831eSSongjun Wu 	struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
2937927831eSSongjun Wu 	struct atmel_hlcd_regs *regs = priv->regs;
2947927831eSSongjun Wu 	struct display_timing *timing = &priv->timing;
2957927831eSSongjun Wu 	struct lcd_dma_desc *desc;
2967927831eSSongjun Wu 	unsigned long value, vl_clk_pol;
2977927831eSSongjun Wu 	int ret;
2987927831eSSongjun Wu 
2997927831eSSongjun Wu 	/* Disable DISP signal */
3007927831eSSongjun Wu 	writel(LCDC_LCDDIS_DISPDIS, &regs->lcdc_lcddis);
30148263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
3027927831eSSongjun Wu 				false, 1000, false);
3037927831eSSongjun Wu 	if (ret)
3047927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
3057927831eSSongjun Wu 	/* Disable synchronization */
3067927831eSSongjun Wu 	writel(LCDC_LCDDIS_SYNCDIS, &regs->lcdc_lcddis);
30748263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
3087927831eSSongjun Wu 				false, 1000, false);
3097927831eSSongjun Wu 	if (ret)
3107927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
3117927831eSSongjun Wu 	/* Disable pixel clock */
3127927831eSSongjun Wu 	writel(LCDC_LCDDIS_CLKDIS, &regs->lcdc_lcddis);
31348263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
3147927831eSSongjun Wu 				false, 1000, false);
3157927831eSSongjun Wu 	if (ret)
3167927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
3177927831eSSongjun Wu 	/* Disable PWM */
3187927831eSSongjun Wu 	writel(LCDC_LCDDIS_PWMDIS, &regs->lcdc_lcddis);
31948263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
3207927831eSSongjun Wu 				false, 1000, false);
3217927831eSSongjun Wu 	if (ret)
3227927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
3237927831eSSongjun Wu 
3247927831eSSongjun Wu 	/* Set pixel clock */
3257927831eSSongjun Wu 	value = priv->clk_rate / timing->pixelclock.typ;
3267927831eSSongjun Wu 	if (priv->clk_rate % timing->pixelclock.typ)
3277927831eSSongjun Wu 		value++;
3287927831eSSongjun Wu 
3297927831eSSongjun Wu 	vl_clk_pol = 0;
3307927831eSSongjun Wu 	if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
3317927831eSSongjun Wu 		vl_clk_pol = LCDC_LCDCFG0_CLKPOL;
3327927831eSSongjun Wu 
3337927831eSSongjun Wu 	if (value < 1) {
3347927831eSSongjun Wu 		/* Using system clock as pixel clock */
3357927831eSSongjun Wu 		writel(LCDC_LCDCFG0_CLKDIV(0)
3367927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISHCR
3377927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISHEO
3387927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISOVR1
3397927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISBASE
3407927831eSSongjun Wu 			| vl_clk_pol
3417927831eSSongjun Wu 			| LCDC_LCDCFG0_CLKSEL,
3427927831eSSongjun Wu 			&regs->lcdc_lcdcfg0);
3437927831eSSongjun Wu 
3447927831eSSongjun Wu 	} else {
3457927831eSSongjun Wu 		writel(LCDC_LCDCFG0_CLKDIV(value - 2)
3467927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISHCR
3477927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISHEO
3487927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISOVR1
3497927831eSSongjun Wu 			| LCDC_LCDCFG0_CGDISBASE
3507927831eSSongjun Wu 			| vl_clk_pol,
3517927831eSSongjun Wu 			&regs->lcdc_lcdcfg0);
3527927831eSSongjun Wu 	}
3537927831eSSongjun Wu 
3547927831eSSongjun Wu 	/* Initialize control register 5 */
3557927831eSSongjun Wu 	value = 0;
3567927831eSSongjun Wu 
3577927831eSSongjun Wu 	if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
3587927831eSSongjun Wu 		value |= LCDC_LCDCFG5_HSPOL;
3597927831eSSongjun Wu 	if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
3607927831eSSongjun Wu 		value |= LCDC_LCDCFG5_VSPOL;
3617927831eSSongjun Wu 
3627927831eSSongjun Wu 	switch (priv->output_mode) {
3637927831eSSongjun Wu 	case 12:
3647927831eSSongjun Wu 		value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP;
3657927831eSSongjun Wu 		break;
3667927831eSSongjun Wu 	case 16:
3677927831eSSongjun Wu 		value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP;
3687927831eSSongjun Wu 		break;
3697927831eSSongjun Wu 	case 18:
3707927831eSSongjun Wu 		value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP;
3717927831eSSongjun Wu 		break;
3727927831eSSongjun Wu 	case 24:
3737927831eSSongjun Wu 		value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
3747927831eSSongjun Wu 		break;
3757927831eSSongjun Wu 	default:
3767927831eSSongjun Wu 		BUG();
3777927831eSSongjun Wu 		break;
3787927831eSSongjun Wu 	}
3797927831eSSongjun Wu 
3807927831eSSongjun Wu 	value |= LCDC_LCDCFG5_GUARDTIME(priv->guard_time);
3817927831eSSongjun Wu 	value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS);
3827927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg5);
3837927831eSSongjun Wu 
3847927831eSSongjun Wu 	/* Vertical & Horizontal Timing */
3857927831eSSongjun Wu 	value = LCDC_LCDCFG1_VSPW(timing->vsync_len.typ - 1);
3867927831eSSongjun Wu 	value |= LCDC_LCDCFG1_HSPW(timing->hsync_len.typ - 1);
3877927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg1);
3887927831eSSongjun Wu 
3897927831eSSongjun Wu 	value = LCDC_LCDCFG2_VBPW(timing->vback_porch.typ);
3907927831eSSongjun Wu 	value |= LCDC_LCDCFG2_VFPW(timing->vfront_porch.typ - 1);
3917927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg2);
3927927831eSSongjun Wu 
3937927831eSSongjun Wu 	value = LCDC_LCDCFG3_HBPW(timing->hback_porch.typ - 1);
3947927831eSSongjun Wu 	value |= LCDC_LCDCFG3_HFPW(timing->hfront_porch.typ - 1);
3957927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg3);
3967927831eSSongjun Wu 
3977927831eSSongjun Wu 	/* Display size */
3987927831eSSongjun Wu 	value = LCDC_LCDCFG4_RPF(timing->vactive.typ - 1);
3997927831eSSongjun Wu 	value |= LCDC_LCDCFG4_PPL(timing->hactive.typ - 1);
4007927831eSSongjun Wu 	writel(value, &regs->lcdc_lcdcfg4);
4017927831eSSongjun Wu 
4027927831eSSongjun Wu 	writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
4037927831eSSongjun Wu 	       &regs->lcdc_basecfg0);
4047927831eSSongjun Wu 
4057927831eSSongjun Wu 	switch (VNBITS(priv->vl_bpix)) {
4067927831eSSongjun Wu 	case 16:
4077927831eSSongjun Wu 		writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565,
4087927831eSSongjun Wu 		       &regs->lcdc_basecfg1);
4097927831eSSongjun Wu 		break;
4107927831eSSongjun Wu 	case 32:
4117927831eSSongjun Wu 		writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
4127927831eSSongjun Wu 		       &regs->lcdc_basecfg1);
4137927831eSSongjun Wu 		break;
4147927831eSSongjun Wu 	default:
4157927831eSSongjun Wu 		BUG();
4167927831eSSongjun Wu 		break;
4177927831eSSongjun Wu 	}
4187927831eSSongjun Wu 
4197927831eSSongjun Wu 	writel(LCDC_BASECFG2_XSTRIDE(0), &regs->lcdc_basecfg2);
4207927831eSSongjun Wu 	writel(0, &regs->lcdc_basecfg3);
4217927831eSSongjun Wu 	writel(LCDC_BASECFG4_DMA, &regs->lcdc_basecfg4);
4227927831eSSongjun Wu 
4237927831eSSongjun Wu 	/* Disable all interrupts */
4247927831eSSongjun Wu 	writel(~0UL, &regs->lcdc_lcdidr);
4257927831eSSongjun Wu 	writel(~0UL, &regs->lcdc_baseidr);
4267927831eSSongjun Wu 
4277927831eSSongjun Wu 	/* Setup the DMA descriptor, this descriptor will loop to itself */
42831e5c892SWenyou Yang 	desc = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*desc));
42931e5c892SWenyou Yang 	if (!desc)
43031e5c892SWenyou Yang 		return;
4317927831eSSongjun Wu 
4327927831eSSongjun Wu 	desc->address = (u32)uc_plat->base;
4337927831eSSongjun Wu 
4347927831eSSongjun Wu 	/* Disable DMA transfer interrupt & descriptor loaded interrupt. */
4357927831eSSongjun Wu 	desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN
4367927831eSSongjun Wu 			| LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH;
4377927831eSSongjun Wu 	desc->next = (u32)desc;
4387927831eSSongjun Wu 
4397927831eSSongjun Wu 	/* Flush the DMA descriptor if we enabled dcache */
44031e5c892SWenyou Yang 	flush_dcache_range((u32)desc,
44131e5c892SWenyou Yang 			   ALIGN(((u32)desc + sizeof(*desc)),
44231e5c892SWenyou Yang 			   CONFIG_SYS_CACHELINE_SIZE));
4437927831eSSongjun Wu 
4447927831eSSongjun Wu 	writel(desc->address, &regs->lcdc_baseaddr);
4457927831eSSongjun Wu 	writel(desc->control, &regs->lcdc_basectrl);
4467927831eSSongjun Wu 	writel(desc->next, &regs->lcdc_basenext);
4477927831eSSongjun Wu 	writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
4487927831eSSongjun Wu 	       &regs->lcdc_basecher);
4497927831eSSongjun Wu 
4507927831eSSongjun Wu 	/* Enable LCD */
4517927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
4527927831eSSongjun Wu 	writel(value | LCDC_LCDEN_CLKEN, &regs->lcdc_lcden);
45348263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_CLKSTS,
4547927831eSSongjun Wu 				true, 1000, false);
4557927831eSSongjun Wu 	if (ret)
4567927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
4577927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
4587927831eSSongjun Wu 	writel(value | LCDC_LCDEN_SYNCEN, &regs->lcdc_lcden);
45948263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_LCDSTS,
4607927831eSSongjun Wu 				true, 1000, false);
4617927831eSSongjun Wu 	if (ret)
4627927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
4637927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
4647927831eSSongjun Wu 	writel(value | LCDC_LCDEN_DISPEN, &regs->lcdc_lcden);
46548263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_DISPSTS,
4667927831eSSongjun Wu 				true, 1000, false);
4677927831eSSongjun Wu 	if (ret)
4687927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
4697927831eSSongjun Wu 	value = readl(&regs->lcdc_lcden);
4707927831eSSongjun Wu 	writel(value | LCDC_LCDEN_PWMEN, &regs->lcdc_lcden);
47148263504SÁlvaro Fernández Rojas 	ret = wait_for_bit_le32(&regs->lcdc_lcdsr, LCDC_LCDSR_PWMSTS,
4727927831eSSongjun Wu 				true, 1000, false);
4737927831eSSongjun Wu 	if (ret)
4747927831eSSongjun Wu 		printf("%s: %d: Timeout!\n", __func__, __LINE__);
4757927831eSSongjun Wu }
4767927831eSSongjun Wu 
atmel_hlcdc_probe(struct udevice * dev)4777927831eSSongjun Wu static int atmel_hlcdc_probe(struct udevice *dev)
4787927831eSSongjun Wu {
4797927831eSSongjun Wu 	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
4807927831eSSongjun Wu 	struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
4817927831eSSongjun Wu 	int ret;
4827927831eSSongjun Wu 
4837927831eSSongjun Wu 	ret = at91_hlcdc_enable_clk(dev);
4847927831eSSongjun Wu 	if (ret)
4857927831eSSongjun Wu 		return ret;
4867927831eSSongjun Wu 
4877927831eSSongjun Wu 	atmel_hlcdc_init(dev);
4887927831eSSongjun Wu 
4897927831eSSongjun Wu 	uc_priv->xsize = priv->timing.hactive.typ;
4907927831eSSongjun Wu 	uc_priv->ysize = priv->timing.vactive.typ;
4917927831eSSongjun Wu 	uc_priv->bpix = priv->vl_bpix;
4927927831eSSongjun Wu 
4937927831eSSongjun Wu 	/* Enable flushing if we enabled dcache */
4947927831eSSongjun Wu 	video_set_flush_dcache(dev, true);
4957927831eSSongjun Wu 
4967927831eSSongjun Wu 	return 0;
4977927831eSSongjun Wu }
4987927831eSSongjun Wu 
atmel_hlcdc_ofdata_to_platdata(struct udevice * dev)4997927831eSSongjun Wu static int atmel_hlcdc_ofdata_to_platdata(struct udevice *dev)
5007927831eSSongjun Wu {
5017927831eSSongjun Wu 	struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
5027927831eSSongjun Wu 	const void *blob = gd->fdt_blob;
503da409cccSSimon Glass 	int node = dev_of_offset(dev);
5047927831eSSongjun Wu 
505a821c4afSSimon Glass 	priv->regs = (struct atmel_hlcd_regs *)devfdt_get_addr(dev);
5067927831eSSongjun Wu 	if (!priv->regs) {
5077927831eSSongjun Wu 		debug("%s: No display controller address\n", __func__);
5087927831eSSongjun Wu 		return -EINVAL;
5097927831eSSongjun Wu 	}
5107927831eSSongjun Wu 
511da409cccSSimon Glass 	if (fdtdec_decode_display_timing(blob, dev_of_offset(dev),
5127927831eSSongjun Wu 					 0, &priv->timing)) {
5137927831eSSongjun Wu 		debug("%s: Failed to decode display timing\n", __func__);
5147927831eSSongjun Wu 		return -EINVAL;
5157927831eSSongjun Wu 	}
5167927831eSSongjun Wu 
5177927831eSSongjun Wu 	if (priv->timing.hactive.typ > LCD_MAX_WIDTH)
5187927831eSSongjun Wu 		priv->timing.hactive.typ = LCD_MAX_WIDTH;
5197927831eSSongjun Wu 
5207927831eSSongjun Wu 	if (priv->timing.vactive.typ > LCD_MAX_HEIGHT)
5217927831eSSongjun Wu 		priv->timing.vactive.typ = LCD_MAX_HEIGHT;
5227927831eSSongjun Wu 
5237927831eSSongjun Wu 	priv->vl_bpix = fdtdec_get_int(blob, node, "atmel,vl-bpix", 0);
5247927831eSSongjun Wu 	if (!priv->vl_bpix) {
5257927831eSSongjun Wu 		debug("%s: Failed to get bits per pixel\n", __func__);
5267927831eSSongjun Wu 		return -EINVAL;
5277927831eSSongjun Wu 	}
5287927831eSSongjun Wu 
5297927831eSSongjun Wu 	priv->output_mode = fdtdec_get_int(blob, node, "atmel,output-mode", 24);
5307927831eSSongjun Wu 	priv->guard_time = fdtdec_get_int(blob, node, "atmel,guard-time", 1);
5317927831eSSongjun Wu 
5327927831eSSongjun Wu 	return 0;
5337927831eSSongjun Wu }
5347927831eSSongjun Wu 
atmel_hlcdc_bind(struct udevice * dev)5357927831eSSongjun Wu static int atmel_hlcdc_bind(struct udevice *dev)
5367927831eSSongjun Wu {
5377927831eSSongjun Wu 	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
5387927831eSSongjun Wu 
5397927831eSSongjun Wu 	uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
5407927831eSSongjun Wu 				(1 << LCD_MAX_LOG2_BPP) / 8;
5417927831eSSongjun Wu 
5427927831eSSongjun Wu 	debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
5437927831eSSongjun Wu 
5447927831eSSongjun Wu 	return 0;
5457927831eSSongjun Wu }
5467927831eSSongjun Wu 
5477927831eSSongjun Wu static const struct udevice_id atmel_hlcdc_ids[] = {
5487927831eSSongjun Wu 	{ .compatible = "atmel,sama5d2-hlcdc" },
5497927831eSSongjun Wu 	{ .compatible = "atmel,at91sam9x5-hlcdc" },
5507927831eSSongjun Wu 	{ }
5517927831eSSongjun Wu };
5527927831eSSongjun Wu 
5537927831eSSongjun Wu U_BOOT_DRIVER(atmel_hlcdfb) = {
5547927831eSSongjun Wu 	.name	= "atmel_hlcdfb",
5557927831eSSongjun Wu 	.id	= UCLASS_VIDEO,
5567927831eSSongjun Wu 	.of_match = atmel_hlcdc_ids,
5577927831eSSongjun Wu 	.bind	= atmel_hlcdc_bind,
5587927831eSSongjun Wu 	.probe	= atmel_hlcdc_probe,
5597927831eSSongjun Wu 	.ofdata_to_platdata = atmel_hlcdc_ofdata_to_platdata,
5607927831eSSongjun Wu 	.priv_auto_alloc_size = sizeof(struct atmel_hlcdc_priv),
5617927831eSSongjun Wu };
5627927831eSSongjun Wu 
5637927831eSSongjun Wu #endif
564