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, ®s->lcdc_lcddis);
7248263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
7848263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
8448263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
9048263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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 ®s->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 ®s->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, ®s->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, ®s->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, ®s->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, ®s->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, ®s->lcdc_lcdcfg4);
170f6b690e6SBo Shen
1717927831eSSongjun Wu writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
1727927831eSSongjun Wu ®s->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 ®s->lcdc_basecfg1);
178f6b690e6SBo Shen break;
1798c1b7172SMarek Vasut case 32:
1807927831eSSongjun Wu writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
1817927831eSSongjun Wu ®s->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), ®s->lcdc_basecfg2);
1897927831eSSongjun Wu writel(0, ®s->lcdc_basecfg3);
1907927831eSSongjun Wu writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4);
191f6b690e6SBo Shen
192f6b690e6SBo Shen /* Disable all interrupts */
1937927831eSSongjun Wu writel(~0UL, ®s->lcdc_lcdidr);
1947927831eSSongjun Wu writel(~0UL, ®s->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, ®s->lcdc_baseaddr);
2097927831eSSongjun Wu writel(desc->control, ®s->lcdc_basectrl);
2107927831eSSongjun Wu writel(desc->next, ®s->lcdc_basenext);
2117927831eSSongjun Wu writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
2127927831eSSongjun Wu ®s->lcdc_basecher);
213f6b690e6SBo Shen
214f6b690e6SBo Shen /* Enable LCD */
2157927831eSSongjun Wu value = readl(®s->lcdc_lcden);
2167927831eSSongjun Wu writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden);
21748263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
2227927831eSSongjun Wu writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden);
22348263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
2287927831eSSongjun Wu writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden);
22948263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
2347927831eSSongjun Wu writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden);
23548263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
30148263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
30748263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
31348263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
31948263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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 ®s->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 ®s->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, ®s->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, ®s->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, ®s->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, ®s->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, ®s->lcdc_lcdcfg4);
4017927831eSSongjun Wu
4027927831eSSongjun Wu writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
4037927831eSSongjun Wu ®s->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 ®s->lcdc_basecfg1);
4097927831eSSongjun Wu break;
4107927831eSSongjun Wu case 32:
4117927831eSSongjun Wu writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
4127927831eSSongjun Wu ®s->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), ®s->lcdc_basecfg2);
4207927831eSSongjun Wu writel(0, ®s->lcdc_basecfg3);
4217927831eSSongjun Wu writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4);
4227927831eSSongjun Wu
4237927831eSSongjun Wu /* Disable all interrupts */
4247927831eSSongjun Wu writel(~0UL, ®s->lcdc_lcdidr);
4257927831eSSongjun Wu writel(~0UL, ®s->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, ®s->lcdc_baseaddr);
4457927831eSSongjun Wu writel(desc->control, ®s->lcdc_basectrl);
4467927831eSSongjun Wu writel(desc->next, ®s->lcdc_basenext);
4477927831eSSongjun Wu writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
4487927831eSSongjun Wu ®s->lcdc_basecher);
4497927831eSSongjun Wu
4507927831eSSongjun Wu /* Enable LCD */
4517927831eSSongjun Wu value = readl(®s->lcdc_lcden);
4527927831eSSongjun Wu writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden);
45348263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
4587927831eSSongjun Wu writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden);
45948263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
4647927831eSSongjun Wu writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden);
46548263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
4707927831eSSongjun Wu writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden);
47148263504SÁlvaro Fernández Rojas ret = wait_for_bit_le32(®s->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