2 * Driver for AT91/AT32 LCD Controller
4 * Copyright (C) 2007 Atmel Corporation
6 * SPDX-License-Identifier: GPL-2.0+
10 #include <atmel_lcd.h>
15 #include <asm/arch/gpio.h>
16 #include <asm/arch/clk.h>
18 #include <bmp_layout.h>
19 #include <atmel_lcdc.h>
21 DECLARE_GLOBAL_DATA_PTR
;
23 #ifdef CONFIG_DM_VIDEO
25 /* Maximum LCD size we support */
28 LCD_MAX_LOG2_BPP
= VIDEO_BPP16
,
32 struct atmel_fb_priv
{
33 struct display_timing timing
;
36 /* configurable parameters */
37 #define ATMEL_LCDC_CVAL_DEFAULT 0xc8
38 #define ATMEL_LCDC_DMA_BURST_LEN 8
39 #ifndef ATMEL_LCDC_GUARD_TIME
40 #define ATMEL_LCDC_GUARD_TIME 1
43 #if defined(CONFIG_AT91SAM9263)
44 #define ATMEL_LCDC_FIFO_SIZE 2048
46 #define ATMEL_LCDC_FIFO_SIZE 512
49 #define lcdc_readl(mmio, reg) __raw_readl((mmio)+(reg))
50 #define lcdc_writel(mmio, reg, val) __raw_writel((val), (mmio)+(reg))
52 #ifndef CONFIG_DM_VIDEO
53 ushort
*configuration_get_cmap(void)
55 return (ushort
*)(panel_info
.mmio
+ ATMEL_LCDC_LUT(0));
58 #if defined(CONFIG_BMP_16BPP) && defined(CONFIG_ATMEL_LCD_BGR555)
59 void fb_put_word(uchar
**fb
, uchar
**from
)
61 *(*fb
)++ = (((*from
)[0] & 0x1f) << 2) | ((*from
)[1] & 0x03);
62 *(*fb
)++ = ((*from
)[0] & 0xe0) | (((*from
)[1] & 0x7c) >> 2);
67 #ifdef CONFIG_LCD_LOGO
69 void lcd_logo_set_cmap(void)
74 uint
*cmap
= (uint
*)configuration_get_cmap();
76 for (i
= 0; i
< BMP_LOGO_COLORS
; ++i
) {
77 colreg
= bmp_logo_palette
[i
];
78 #ifdef CONFIG_ATMEL_LCD_BGR555
79 lut_entry
= ((colreg
& 0x000F) << 11) |
80 ((colreg
& 0x00F0) << 2) |
81 ((colreg
& 0x0F00) >> 7);
83 lut_entry
= ((colreg
& 0x000F) << 1) |
84 ((colreg
& 0x00F0) << 3) |
85 ((colreg
& 0x0F00) << 4);
87 *(cmap
+ BMP_LOGO_OFFSET
) = lut_entry
;
93 void lcd_setcolreg(ushort regno
, ushort red
, ushort green
, ushort blue
)
95 #if defined(CONFIG_ATMEL_LCD_BGR555)
96 lcdc_writel(panel_info
.mmio
, ATMEL_LCDC_LUT(regno
),
97 (red
>> 3) | ((green
& 0xf8) << 2) | ((blue
& 0xf8) << 7));
99 lcdc_writel(panel_info
.mmio
, ATMEL_LCDC_LUT(regno
),
100 (blue
>> 3) | ((green
& 0xfc) << 3) | ((red
& 0xf8) << 8));
104 void lcd_set_cmap(struct bmp_image
*bmp
, unsigned colors
)
108 for (i
= 0; i
< colors
; ++i
) {
109 struct bmp_color_table_entry cte
= bmp
->color_table
[i
];
110 lcd_setcolreg(i
, cte
.red
, cte
.green
, cte
.blue
);
115 static void atmel_fb_init(ulong addr
, struct display_timing
*timing
, int bpix
,
116 bool tft
, bool cont_pol_low
, ulong lcdbase
)
119 void *reg
= (void *)addr
;
121 /* Turn off the LCD controller and the DMA controller */
122 lcdc_writel(reg
, ATMEL_LCDC_PWRCON
,
123 ATMEL_LCDC_GUARD_TIME
<< ATMEL_LCDC_GUARDT_OFFSET
);
125 /* Wait for the LCDC core to become idle */
126 while (lcdc_readl(reg
, ATMEL_LCDC_PWRCON
) & ATMEL_LCDC_BUSY
)
129 lcdc_writel(reg
, ATMEL_LCDC_DMACON
, 0);
132 lcdc_writel(reg
, ATMEL_LCDC_DMACON
, ATMEL_LCDC_DMARST
);
134 /* ...set frame size and burst length = 8 words (?) */
135 value
= (timing
->hactive
.typ
* timing
->vactive
.typ
*
137 value
|= ((ATMEL_LCDC_DMA_BURST_LEN
- 1) << ATMEL_LCDC_BLENGTH_OFFSET
);
138 lcdc_writel(reg
, ATMEL_LCDC_DMAFRMCFG
, value
);
140 /* Set pixel clock */
141 value
= get_lcdc_clk_rate(0) / timing
->pixelclock
.typ
;
142 if (get_lcdc_clk_rate(0) % timing
->pixelclock
.typ
)
144 value
= (value
/ 2) - 1;
147 lcdc_writel(reg
, ATMEL_LCDC_LCDCON1
, ATMEL_LCDC_BYPASS
);
149 lcdc_writel(reg
, ATMEL_LCDC_LCDCON1
,
150 value
<< ATMEL_LCDC_CLKVAL_OFFSET
);
152 /* Initialize control register 2 */
154 value
= ATMEL_LCDC_MEMOR_BIG
| ATMEL_LCDC_CLKMOD_ALWAYSACTIVE
;
156 value
= ATMEL_LCDC_MEMOR_LITTLE
| ATMEL_LCDC_CLKMOD_ALWAYSACTIVE
;
159 value
|= ATMEL_LCDC_DISTYPE_TFT
;
161 if (!(timing
->flags
& DISPLAY_FLAGS_HSYNC_HIGH
))
162 value
|= ATMEL_LCDC_INVLINE_INVERTED
;
163 if (!(timing
->flags
& DISPLAY_FLAGS_VSYNC_HIGH
))
164 value
|= ATMEL_LCDC_INVFRAME_INVERTED
;
166 lcdc_writel(reg
, ATMEL_LCDC_LCDCON2
, value
);
168 /* Vertical timing */
169 value
= (timing
->vsync_len
.typ
- 1) << ATMEL_LCDC_VPW_OFFSET
;
170 value
|= timing
->vback_porch
.typ
<< ATMEL_LCDC_VBP_OFFSET
;
171 value
|= timing
->vfront_porch
.typ
;
172 /* Magic! (Datasheet says "Bit 31 must be written to 1") */
174 lcdc_writel(reg
, ATMEL_LCDC_TIM1
, value
);
176 /* Horizontal timing */
177 value
= (timing
->hfront_porch
.typ
- 1) << ATMEL_LCDC_HFP_OFFSET
;
178 value
|= (timing
->hsync_len
.typ
- 1) << ATMEL_LCDC_HPW_OFFSET
;
179 value
|= (timing
->hback_porch
.typ
- 1);
180 lcdc_writel(reg
, ATMEL_LCDC_TIM2
, value
);
183 value
= (timing
->hactive
.typ
- 1) << ATMEL_LCDC_HOZVAL_OFFSET
;
184 value
|= timing
->vactive
.typ
- 1;
185 lcdc_writel(reg
, ATMEL_LCDC_LCDFRMCFG
, value
);
187 /* FIFO Threshold: Use formula from data sheet */
188 value
= ATMEL_LCDC_FIFO_SIZE
- (2 * ATMEL_LCDC_DMA_BURST_LEN
+ 3);
189 lcdc_writel(reg
, ATMEL_LCDC_FIFO
, value
);
191 /* Toggle LCD_MODE every frame */
192 lcdc_writel(reg
, ATMEL_LCDC_MVAL
, 0);
194 /* Disable all interrupts */
195 lcdc_writel(reg
, ATMEL_LCDC_IDR
, ~0UL);
198 value
= ATMEL_LCDC_PS_DIV8
|
199 ATMEL_LCDC_ENA_PWMENABLE
;
201 value
|= ATMEL_LCDC_POL_POSITIVE
;
202 lcdc_writel(reg
, ATMEL_LCDC_CONTRAST_CTR
, value
);
203 lcdc_writel(reg
, ATMEL_LCDC_CONTRAST_VAL
, ATMEL_LCDC_CVAL_DEFAULT
);
205 /* Set framebuffer DMA base address and pixel offset */
206 lcdc_writel(reg
, ATMEL_LCDC_DMABADDR1
, lcdbase
);
208 lcdc_writel(reg
, ATMEL_LCDC_DMACON
, ATMEL_LCDC_DMAEN
);
209 lcdc_writel(reg
, ATMEL_LCDC_PWRCON
,
210 (ATMEL_LCDC_GUARD_TIME
<< ATMEL_LCDC_GUARDT_OFFSET
) | ATMEL_LCDC_PWR
);
213 #ifndef CONFIG_DM_VIDEO
214 void lcd_ctrl_init(void *lcdbase
)
216 struct display_timing timing
;
219 if (!(panel_info
.vl_sync
& ATMEL_LCDC_INVLINE_INVERTED
))
220 timing
.flags
|= DISPLAY_FLAGS_HSYNC_HIGH
;
221 if (!(panel_info
.vl_sync
& ATMEL_LCDC_INVFRAME_INVERTED
))
222 timing
.flags
|= DISPLAY_FLAGS_VSYNC_LOW
;
223 timing
.pixelclock
.typ
= panel_info
.vl_clk
;
225 timing
.hactive
.typ
= panel_info
.vl_col
;
226 timing
.hfront_porch
.typ
= panel_info
.vl_right_margin
;
227 timing
.hback_porch
.typ
= panel_info
.vl_left_margin
;
228 timing
.hsync_len
.typ
= panel_info
.vl_hsync_len
;
230 timing
.vactive
.typ
= panel_info
.vl_row
;
231 timing
.vfront_porch
.typ
= panel_info
.vl_clk
;
232 timing
.vback_porch
.typ
= panel_info
.vl_clk
;
233 timing
.vsync_len
.typ
= panel_info
.vl_clk
;
235 atmel_fb_init(panel_info
.mmio
, &timing
, panel_info
.vl_bpix
,
236 panel_info
.vl_tft
, panel_info
.vl_cont_pol_low
,
240 ulong
calc_fbsize(void)
242 return ((panel_info
.vl_col
* panel_info
.vl_row
*
243 NBITS(panel_info
.vl_bpix
)) / 8) + PAGE_SIZE
;
247 #ifdef CONFIG_DM_VIDEO
248 static int atmel_fb_lcd_probe(struct udevice
*dev
)
250 struct video_uc_platdata
*uc_plat
= dev_get_uclass_platdata(dev
);
251 struct video_priv
*uc_priv
= dev_get_uclass_priv(dev
);
252 struct atmel_fb_priv
*priv
= dev_get_priv(dev
);
253 struct display_timing
*timing
= &priv
->timing
;
256 * For now some values are hard-coded. We could use the device tree
257 * bindings in simple-framebuffer.txt to specify the format/bpp and
258 * some Atmel-specific binding for tft and cont_pol_low.
260 atmel_fb_init(ATMEL_BASE_LCDC
, timing
, VIDEO_BPP16
, true, false,
262 uc_priv
->xsize
= timing
->hactive
.typ
;
263 uc_priv
->ysize
= timing
->vactive
.typ
;
264 uc_priv
->bpix
= VIDEO_BPP16
;
265 video_set_flush_dcache(dev
, true);
266 debug("LCD frame buffer at %lx, size %x, %dx%d pixels\n", uc_plat
->base
,
267 uc_plat
->size
, uc_priv
->xsize
, uc_priv
->ysize
);
272 static int atmel_fb_ofdata_to_platdata(struct udevice
*dev
)
274 struct atmel_lcd_platdata
*plat
= dev_get_platdata(dev
);
275 struct atmel_fb_priv
*priv
= dev_get_priv(dev
);
276 struct display_timing
*timing
= &priv
->timing
;
277 const void *blob
= gd
->fdt_blob
;
279 if (fdtdec_decode_display_timing(blob
, dev
->of_offset
,
280 plat
->timing_index
, timing
)) {
281 debug("%s: Failed to decode display timing\n", __func__
);
288 static int atmel_fb_lcd_bind(struct udevice
*dev
)
290 struct video_uc_platdata
*uc_plat
= dev_get_uclass_platdata(dev
);
292 uc_plat
->size
= LCD_MAX_WIDTH
* LCD_MAX_HEIGHT
*
293 (1 << VIDEO_BPP16
) / 8;
294 debug("%s: Frame buffer size %x\n", __func__
, uc_plat
->size
);
299 static const struct udevice_id atmel_fb_lcd_ids
[] = {
300 { .compatible
= "atmel,at91sam9g45-lcdc" },
304 U_BOOT_DRIVER(atmel_fb
) = {
307 .of_match
= atmel_fb_lcd_ids
,
308 .bind
= atmel_fb_lcd_bind
,
309 .ofdata_to_platdata
= atmel_fb_ofdata_to_platdata
,
310 .probe
= atmel_fb_lcd_probe
,
311 .platdata_auto_alloc_size
= sizeof(struct atmel_lcd_platdata
),
312 .priv_auto_alloc_size
= sizeof(struct atmel_fb_priv
),