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, ®s->lcdc_lcddis);
73*b491b498SJon Lin ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
79*b491b498SJon Lin ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
85*b491b498SJon Lin ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
91*b491b498SJon Lin ret = wait_for_bit_le32(®s->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 ®s->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 ®s->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, ®s->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, ®s->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, ®s->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, ®s->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, ®s->lcdc_lcdcfg4);
171f6b690e6SBo Shen
1727927831eSSongjun Wu writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
1737927831eSSongjun Wu ®s->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 ®s->lcdc_basecfg1);
179f6b690e6SBo Shen break;
1808c1b7172SMarek Vasut case 32:
1817927831eSSongjun Wu writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
1827927831eSSongjun Wu ®s->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), ®s->lcdc_basecfg2);
1907927831eSSongjun Wu writel(0, ®s->lcdc_basecfg3);
1917927831eSSongjun Wu writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4);
192f6b690e6SBo Shen
193f6b690e6SBo Shen /* Disable all interrupts */
1947927831eSSongjun Wu writel(~0UL, ®s->lcdc_lcdidr);
1957927831eSSongjun Wu writel(~0UL, ®s->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, ®s->lcdc_baseaddr);
2107927831eSSongjun Wu writel(desc->control, ®s->lcdc_basectrl);
2117927831eSSongjun Wu writel(desc->next, ®s->lcdc_basenext);
2127927831eSSongjun Wu writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
2137927831eSSongjun Wu ®s->lcdc_basecher);
214f6b690e6SBo Shen
215f6b690e6SBo Shen /* Enable LCD */
2167927831eSSongjun Wu value = readl(®s->lcdc_lcden);
2177927831eSSongjun Wu writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden);
218*b491b498SJon Lin ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
2237927831eSSongjun Wu writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden);
224*b491b498SJon Lin ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
2297927831eSSongjun Wu writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden);
230*b491b498SJon Lin ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
2357927831eSSongjun Wu writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden);
236*b491b498SJon Lin ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
302*b491b498SJon Lin ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
308*b491b498SJon Lin ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
314*b491b498SJon Lin ret = wait_for_bit_le32(®s->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, ®s->lcdc_lcddis);
320*b491b498SJon Lin ret = wait_for_bit_le32(®s->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 ®s->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 ®s->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, ®s->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, ®s->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, ®s->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, ®s->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, ®s->lcdc_lcdcfg4);
4027927831eSSongjun Wu
4037927831eSSongjun Wu writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO,
4047927831eSSongjun Wu ®s->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 ®s->lcdc_basecfg1);
4107927831eSSongjun Wu break;
4117927831eSSongjun Wu case 32:
4127927831eSSongjun Wu writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888,
4137927831eSSongjun Wu ®s->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), ®s->lcdc_basecfg2);
4217927831eSSongjun Wu writel(0, ®s->lcdc_basecfg3);
4227927831eSSongjun Wu writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4);
4237927831eSSongjun Wu
4247927831eSSongjun Wu /* Disable all interrupts */
4257927831eSSongjun Wu writel(~0UL, ®s->lcdc_lcdidr);
4267927831eSSongjun Wu writel(~0UL, ®s->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, ®s->lcdc_baseaddr);
4467927831eSSongjun Wu writel(desc->control, ®s->lcdc_basectrl);
4477927831eSSongjun Wu writel(desc->next, ®s->lcdc_basenext);
4487927831eSSongjun Wu writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN,
4497927831eSSongjun Wu ®s->lcdc_basecher);
4507927831eSSongjun Wu
4517927831eSSongjun Wu /* Enable LCD */
4527927831eSSongjun Wu value = readl(®s->lcdc_lcden);
4537927831eSSongjun Wu writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden);
454*b491b498SJon Lin ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
4597927831eSSongjun Wu writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden);
460*b491b498SJon Lin ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
4657927831eSSongjun Wu writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden);
466*b491b498SJon Lin ret = wait_for_bit_le32(®s->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(®s->lcdc_lcden);
4717927831eSSongjun Wu writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden);
472*b491b498SJon Lin ret = wait_for_bit_le32(®s->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