2 * Driver for AT91/AT32 LCD Controller
4 * Copyright (C) 2007 Atmel Corporation
6 * SPDX-License-Identifier: GPL-2.0+
12 #include <asm/arch/gpio.h>
13 #include <asm/arch/clk.h>
15 #include <bmp_layout.h>
16 #include <atmel_lcdc.h>
18 /* configurable parameters */
19 #define ATMEL_LCDC_CVAL_DEFAULT 0xc8
20 #define ATMEL_LCDC_DMA_BURST_LEN 8
21 #ifndef ATMEL_LCDC_GUARD_TIME
22 #define ATMEL_LCDC_GUARD_TIME 1
25 #if defined(CONFIG_AT91SAM9263)
26 #define ATMEL_LCDC_FIFO_SIZE 2048
28 #define ATMEL_LCDC_FIFO_SIZE 512
31 #define lcdc_readl(mmio, reg) __raw_readl((mmio)+(reg))
32 #define lcdc_writel(mmio, reg, val) __raw_writel((val), (mmio)+(reg))
34 ushort *configuration_get_cmap(void)
36 return (ushort *)(panel_info.mmio + ATMEL_LCDC_LUT(0));
39 #if defined(CONFIG_BMP_16BPP) && defined(CONFIG_ATMEL_LCD_BGR555)
40 void fb_put_word(uchar **fb, uchar **from)
42 *(*fb)++ = (((*from)[0] & 0x1f) << 2) | ((*from)[1] & 0x03);
43 *(*fb)++ = ((*from)[0] & 0xe0) | (((*from)[1] & 0x7c) >> 2);
48 #ifdef CONFIG_LCD_LOGO
50 void lcd_logo_set_cmap(void)
55 uint *cmap = (uint *)configuration_get_cmap();
57 for (i = 0; i < BMP_LOGO_COLORS; ++i) {
58 colreg = bmp_logo_palette[i];
59 #ifdef CONFIG_ATMEL_LCD_BGR555
60 lut_entry = ((colreg & 0x000F) << 11) |
61 ((colreg & 0x00F0) << 2) |
62 ((colreg & 0x0F00) >> 7);
64 lut_entry = ((colreg & 0x000F) << 1) |
65 ((colreg & 0x00F0) << 3) |
66 ((colreg & 0x0F00) << 4);
68 *(cmap + BMP_LOGO_OFFSET) = lut_entry;
74 void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
76 #if defined(CONFIG_ATMEL_LCD_BGR555)
77 lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
78 (red >> 3) | ((green & 0xf8) << 2) | ((blue & 0xf8) << 7));
80 lcdc_writel(panel_info.mmio, ATMEL_LCDC_LUT(regno),
81 (blue >> 3) | ((green & 0xfc) << 3) | ((red & 0xf8) << 8));
85 void lcd_set_cmap(struct bmp_image *bmp, unsigned colors)
89 for (i = 0; i < colors; ++i) {
90 struct bmp_color_table_entry cte = bmp->color_table[i];
91 lcd_setcolreg(i, cte.red, cte.green, cte.blue);
95 static void atmel_fb_init(ulong addr, struct display_timing *timing, int bpix,
96 bool tft, bool cont_pol_low, ulong lcdbase)
99 void *reg = (void *)addr;
101 /* Turn off the LCD controller and the DMA controller */
102 lcdc_writel(reg, ATMEL_LCDC_PWRCON,
103 ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET);
105 /* Wait for the LCDC core to become idle */
106 while (lcdc_readl(reg, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
109 lcdc_writel(reg, ATMEL_LCDC_DMACON, 0);
112 lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST);
114 /* ...set frame size and burst length = 8 words (?) */
115 value = (timing->hactive.typ * timing->vactive.typ *
117 value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);
118 lcdc_writel(reg, ATMEL_LCDC_DMAFRMCFG, value);
120 /* Set pixel clock */
121 value = get_lcdc_clk_rate(0) / timing->pixelclock.typ;
122 if (get_lcdc_clk_rate(0) % timing->pixelclock.typ)
124 value = (value / 2) - 1;
127 lcdc_writel(reg, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
129 lcdc_writel(reg, ATMEL_LCDC_LCDCON1,
130 value << ATMEL_LCDC_CLKVAL_OFFSET);
132 /* Initialize control register 2 */
134 value = ATMEL_LCDC_MEMOR_BIG | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
136 value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
139 value |= ATMEL_LCDC_DISTYPE_TFT;
141 if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
142 value |= ATMEL_LCDC_INVLINE_INVERTED;
143 if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
144 value |= ATMEL_LCDC_INVFRAME_INVERTED;
146 lcdc_writel(reg, ATMEL_LCDC_LCDCON2, value);
148 /* Vertical timing */
149 value = (timing->vsync_len.typ - 1) << ATMEL_LCDC_VPW_OFFSET;
150 value |= timing->vback_porch.typ << ATMEL_LCDC_VBP_OFFSET;
151 value |= timing->vfront_porch.typ;
152 /* Magic! (Datasheet says "Bit 31 must be written to 1") */
154 lcdc_writel(reg, ATMEL_LCDC_TIM1, value);
156 /* Horizontal timing */
157 value = (timing->hfront_porch.typ - 1) << ATMEL_LCDC_HFP_OFFSET;
158 value |= (timing->hsync_len.typ - 1) << ATMEL_LCDC_HPW_OFFSET;
159 value |= (timing->hback_porch.typ - 1);
160 lcdc_writel(reg, ATMEL_LCDC_TIM2, value);
163 value = (timing->hactive.typ - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
164 value |= timing->vactive.typ - 1;
165 lcdc_writel(reg, ATMEL_LCDC_LCDFRMCFG, value);
167 /* FIFO Threshold: Use formula from data sheet */
168 value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
169 lcdc_writel(reg, ATMEL_LCDC_FIFO, value);
171 /* Toggle LCD_MODE every frame */
172 lcdc_writel(reg, ATMEL_LCDC_MVAL, 0);
174 /* Disable all interrupts */
175 lcdc_writel(reg, ATMEL_LCDC_IDR, ~0UL);
178 value = ATMEL_LCDC_PS_DIV8 |
179 ATMEL_LCDC_ENA_PWMENABLE;
181 value |= ATMEL_LCDC_POL_POSITIVE;
182 lcdc_writel(reg, ATMEL_LCDC_CONTRAST_CTR, value);
183 lcdc_writel(reg, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
185 /* Set framebuffer DMA base address and pixel offset */
186 lcdc_writel(reg, ATMEL_LCDC_DMABADDR1, lcdbase);
188 lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN);
189 lcdc_writel(reg, ATMEL_LCDC_PWRCON,
190 (ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
193 void lcd_ctrl_init(void *lcdbase)
195 struct display_timing timing;
198 if (!(panel_info.vl_sync & ATMEL_LCDC_INVLINE_INVERTED))
199 timing.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
200 if (!(panel_info.vl_sync & ATMEL_LCDC_INVFRAME_INVERTED))
201 timing.flags |= DISPLAY_FLAGS_VSYNC_LOW;
202 timing.pixelclock.typ = panel_info.vl_clk;
204 timing.hactive.typ = panel_info.vl_col;
205 timing.hfront_porch.typ = panel_info.vl_right_margin;
206 timing.hback_porch.typ = panel_info.vl_left_margin;
207 timing.hsync_len.typ = panel_info.vl_hsync_len;
209 timing.vactive.typ = panel_info.vl_row;
210 timing.vfront_porch.typ = panel_info.vl_clk;
211 timing.vback_porch.typ = panel_info.vl_clk;
212 timing.vsync_len.typ = panel_info.vl_clk;
214 atmel_fb_init(panel_info.mmio, &timing, panel_info.vl_bpix,
215 panel_info.vl_tft, panel_info.vl_cont_pol_low,
219 ulong calc_fbsize(void)
221 return ((panel_info.vl_col * panel_info.vl_row *
222 NBITS(panel_info.vl_bpix)) / 8) + PAGE_SIZE;