]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
f6b690e6 BS |
2 | /* |
3 | * Driver for AT91/AT32 MULTI LAYER LCD Controller | |
4 | * | |
5 | * Copyright (C) 2012 Atmel Corporation | |
f6b690e6 BS |
6 | */ |
7 | ||
8 | #include <common.h> | |
1eb69ae4 | 9 | #include <cpu_func.h> |
f7ae49fc | 10 | #include <log.h> |
336d4615 | 11 | #include <malloc.h> |
e6f6f9e6 | 12 | #include <part.h> |
f6b690e6 BS |
13 | #include <asm/io.h> |
14 | #include <asm/arch/gpio.h> | |
15 | #include <asm/arch/clk.h> | |
7927831e SW |
16 | #include <clk.h> |
17 | #include <dm.h> | |
18 | #include <fdtdec.h> | |
f6b690e6 | 19 | #include <lcd.h> |
7927831e SW |
20 | #include <video.h> |
21 | #include <wait_bit.h> | |
f6b690e6 | 22 | #include <atmel_hlcdc.h> |
eb41d8a1 | 23 | #include <linux/bug.h> |
f6b690e6 | 24 | |
38b55087 NK |
25 | #if defined(CONFIG_LCD_LOGO) |
26 | #include <bmp_logo.h> | |
27 | #endif | |
28 | ||
7927831e SW |
29 | DECLARE_GLOBAL_DATA_PTR; |
30 | ||
31 | #ifndef CONFIG_DM_VIDEO | |
32 | ||
f6b690e6 BS |
33 | /* configurable parameters */ |
34 | #define ATMEL_LCDC_CVAL_DEFAULT 0xc8 | |
35 | #define ATMEL_LCDC_DMA_BURST_LEN 8 | |
36 | #ifndef ATMEL_LCDC_GUARD_TIME | |
37 | #define ATMEL_LCDC_GUARD_TIME 1 | |
38 | #endif | |
39 | ||
40 | #define ATMEL_LCDC_FIFO_SIZE 512 | |
41 | ||
cfcd1c03 BS |
42 | /* |
43 | * the CLUT register map as following | |
44 | * RCLUT(24 ~ 16), GCLUT(15 ~ 8), BCLUT(7 ~ 0) | |
45 | */ | |
46 | void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) | |
47 | { | |
7927831e SW |
48 | writel(panel_info.mmio + ATMEL_LCDC_LUT(regno), |
49 | ((red << LCDC_BASECLUT_RCLUT_Pos) & LCDC_BASECLUT_RCLUT_Msk) | |
50 | | ((green << LCDC_BASECLUT_GCLUT_Pos) & LCDC_BASECLUT_GCLUT_Msk) | |
51 | | ((blue << LCDC_BASECLUT_BCLUT_Pos) & LCDC_BASECLUT_BCLUT_Msk)); | |
cfcd1c03 BS |
52 | } |
53 | ||
38b55087 NK |
54 | ushort *configuration_get_cmap(void) |
55 | { | |
56 | #if defined(CONFIG_LCD_LOGO) | |
57 | return bmp_logo_palette; | |
58 | #else | |
59 | return NULL; | |
60 | #endif | |
61 | } | |
62 | ||
f6b690e6 BS |
63 | void lcd_ctrl_init(void *lcdbase) |
64 | { | |
65 | unsigned long value; | |
66 | struct lcd_dma_desc *desc; | |
67 | struct atmel_hlcd_regs *regs; | |
7927831e | 68 | int ret; |
f6b690e6 BS |
69 | |
70 | if (!has_lcdc()) | |
71 | return; /* No lcdc */ | |
72 | ||
73 | regs = (struct atmel_hlcd_regs *)panel_info.mmio; | |
74 | ||
75 | /* Disable DISP signal */ | |
7927831e | 76 | writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); |
48263504 ÁFR |
77 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, |
78 | false, 1000, false); | |
7927831e SW |
79 | if (ret) |
80 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
f6b690e6 | 81 | /* Disable synchronization */ |
7927831e | 82 | writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); |
48263504 ÁFR |
83 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, |
84 | false, 1000, false); | |
7927831e SW |
85 | if (ret) |
86 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
f6b690e6 | 87 | /* Disable pixel clock */ |
7927831e | 88 | writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); |
48263504 ÁFR |
89 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, |
90 | false, 1000, false); | |
7927831e SW |
91 | if (ret) |
92 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
f6b690e6 | 93 | /* Disable PWM */ |
7927831e | 94 | writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); |
48263504 ÁFR |
95 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, |
96 | false, 1000, false); | |
7927831e SW |
97 | if (ret) |
98 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
f6b690e6 BS |
99 | |
100 | /* Set pixel clock */ | |
101 | value = get_lcdc_clk_rate(0) / panel_info.vl_clk; | |
102 | if (get_lcdc_clk_rate(0) % panel_info.vl_clk) | |
103 | value++; | |
104 | ||
105 | if (value < 1) { | |
106 | /* Using system clock as pixel clock */ | |
7927831e SW |
107 | writel(LCDC_LCDCFG0_CLKDIV(0) |
108 | | LCDC_LCDCFG0_CGDISHCR | |
109 | | LCDC_LCDCFG0_CGDISHEO | |
110 | | LCDC_LCDCFG0_CGDISOVR1 | |
111 | | LCDC_LCDCFG0_CGDISBASE | |
112 | | panel_info.vl_clk_pol | |
113 | | LCDC_LCDCFG0_CLKSEL, | |
114 | ®s->lcdc_lcdcfg0); | |
f6b690e6 BS |
115 | |
116 | } else { | |
7927831e SW |
117 | writel(LCDC_LCDCFG0_CLKDIV(value - 2) |
118 | | LCDC_LCDCFG0_CGDISHCR | |
119 | | LCDC_LCDCFG0_CGDISHEO | |
120 | | LCDC_LCDCFG0_CGDISOVR1 | |
121 | | LCDC_LCDCFG0_CGDISBASE | |
122 | | panel_info.vl_clk_pol, | |
123 | ®s->lcdc_lcdcfg0); | |
f6b690e6 BS |
124 | } |
125 | ||
126 | /* Initialize control register 5 */ | |
127 | value = 0; | |
128 | ||
129 | value |= panel_info.vl_sync; | |
130 | ||
131 | #ifndef LCD_OUTPUT_BPP | |
132 | /* Output is 24bpp */ | |
133 | value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; | |
134 | #else | |
135 | switch (LCD_OUTPUT_BPP) { | |
136 | case 12: | |
137 | value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP; | |
138 | break; | |
139 | case 16: | |
140 | value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP; | |
141 | break; | |
142 | case 18: | |
143 | value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP; | |
144 | break; | |
145 | case 24: | |
146 | value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; | |
147 | break; | |
148 | default: | |
149 | BUG(); | |
150 | break; | |
151 | } | |
152 | #endif | |
153 | ||
154 | value |= LCDC_LCDCFG5_GUARDTIME(ATMEL_LCDC_GUARD_TIME); | |
155 | value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS); | |
7927831e | 156 | writel(value, ®s->lcdc_lcdcfg5); |
f6b690e6 BS |
157 | |
158 | /* Vertical & Horizontal Timing */ | |
159 | value = LCDC_LCDCFG1_VSPW(panel_info.vl_vsync_len - 1); | |
160 | value |= LCDC_LCDCFG1_HSPW(panel_info.vl_hsync_len - 1); | |
7927831e | 161 | writel(value, ®s->lcdc_lcdcfg1); |
f6b690e6 | 162 | |
1161f98d WJ |
163 | value = LCDC_LCDCFG2_VBPW(panel_info.vl_upper_margin); |
164 | value |= LCDC_LCDCFG2_VFPW(panel_info.vl_lower_margin - 1); | |
7927831e | 165 | writel(value, ®s->lcdc_lcdcfg2); |
f6b690e6 | 166 | |
1161f98d WJ |
167 | value = LCDC_LCDCFG3_HBPW(panel_info.vl_left_margin - 1); |
168 | value |= LCDC_LCDCFG3_HFPW(panel_info.vl_right_margin - 1); | |
7927831e | 169 | writel(value, ®s->lcdc_lcdcfg3); |
f6b690e6 BS |
170 | |
171 | /* Display size */ | |
172 | value = LCDC_LCDCFG4_RPF(panel_info.vl_row - 1); | |
173 | value |= LCDC_LCDCFG4_PPL(panel_info.vl_col - 1); | |
7927831e | 174 | writel(value, ®s->lcdc_lcdcfg4); |
f6b690e6 | 175 | |
7927831e SW |
176 | writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO, |
177 | ®s->lcdc_basecfg0); | |
f6b690e6 BS |
178 | |
179 | switch (NBITS(panel_info.vl_bpix)) { | |
180 | case 16: | |
7927831e SW |
181 | writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565, |
182 | ®s->lcdc_basecfg1); | |
f6b690e6 | 183 | break; |
8c1b7172 | 184 | case 32: |
7927831e SW |
185 | writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888, |
186 | ®s->lcdc_basecfg1); | |
8c1b7172 | 187 | break; |
f6b690e6 BS |
188 | default: |
189 | BUG(); | |
190 | break; | |
191 | } | |
192 | ||
7927831e SW |
193 | writel(LCDC_BASECFG2_XSTRIDE(0), ®s->lcdc_basecfg2); |
194 | writel(0, ®s->lcdc_basecfg3); | |
195 | writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4); | |
f6b690e6 BS |
196 | |
197 | /* Disable all interrupts */ | |
7927831e SW |
198 | writel(~0UL, ®s->lcdc_lcdidr); |
199 | writel(~0UL, ®s->lcdc_baseidr); | |
f6b690e6 BS |
200 | |
201 | /* Setup the DMA descriptor, this descriptor will loop to itself */ | |
202 | desc = (struct lcd_dma_desc *)(lcdbase - 16); | |
203 | ||
204 | desc->address = (u32)lcdbase; | |
205 | /* Disable DMA transfer interrupt & descriptor loaded interrupt. */ | |
206 | desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN | |
207 | | LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH; | |
208 | desc->next = (u32)desc; | |
209 | ||
b137bd8c WJ |
210 | /* Flush the DMA descriptor if we enabled dcache */ |
211 | flush_dcache_range((u32)desc, (u32)desc + sizeof(*desc)); | |
212 | ||
7927831e SW |
213 | writel(desc->address, ®s->lcdc_baseaddr); |
214 | writel(desc->control, ®s->lcdc_basectrl); | |
215 | writel(desc->next, ®s->lcdc_basenext); | |
216 | writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN, | |
217 | ®s->lcdc_basecher); | |
f6b690e6 BS |
218 | |
219 | /* Enable LCD */ | |
7927831e SW |
220 | value = readl(®s->lcdc_lcden); |
221 | writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
222 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, |
223 | true, 1000, false); | |
7927831e SW |
224 | if (ret) |
225 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
226 | value = readl(®s->lcdc_lcden); | |
227 | writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
228 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, |
229 | true, 1000, false); | |
7927831e SW |
230 | if (ret) |
231 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
232 | value = readl(®s->lcdc_lcden); | |
233 | writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
234 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, |
235 | true, 1000, false); | |
7927831e SW |
236 | if (ret) |
237 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
238 | value = readl(®s->lcdc_lcden); | |
239 | writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
240 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, |
241 | true, 1000, false); | |
7927831e SW |
242 | if (ret) |
243 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
b137bd8c WJ |
244 | |
245 | /* Enable flushing if we enabled dcache */ | |
246 | lcd_set_flush_dcache(1); | |
f6b690e6 | 247 | } |
7927831e SW |
248 | |
249 | #else | |
250 | ||
251 | enum { | |
252 | LCD_MAX_WIDTH = 1024, | |
253 | LCD_MAX_HEIGHT = 768, | |
254 | LCD_MAX_LOG2_BPP = VIDEO_BPP16, | |
255 | }; | |
256 | ||
257 | struct atmel_hlcdc_priv { | |
258 | struct atmel_hlcd_regs *regs; | |
259 | struct display_timing timing; | |
260 | unsigned int vl_bpix; | |
261 | unsigned int output_mode; | |
262 | unsigned int guard_time; | |
263 | ulong clk_rate; | |
264 | }; | |
265 | ||
266 | static int at91_hlcdc_enable_clk(struct udevice *dev) | |
267 | { | |
268 | struct atmel_hlcdc_priv *priv = dev_get_priv(dev); | |
269 | struct clk clk; | |
270 | ulong clk_rate; | |
271 | int ret; | |
272 | ||
273 | ret = clk_get_by_index(dev, 0, &clk); | |
274 | if (ret) | |
275 | return -EINVAL; | |
276 | ||
277 | ret = clk_enable(&clk); | |
278 | if (ret) | |
279 | return ret; | |
280 | ||
281 | clk_rate = clk_get_rate(&clk); | |
282 | if (!clk_rate) { | |
283 | clk_disable(&clk); | |
284 | return -ENODEV; | |
285 | } | |
286 | ||
287 | priv->clk_rate = clk_rate; | |
288 | ||
289 | clk_free(&clk); | |
290 | ||
291 | return 0; | |
292 | } | |
293 | ||
294 | static void atmel_hlcdc_init(struct udevice *dev) | |
295 | { | |
296 | struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); | |
297 | struct atmel_hlcdc_priv *priv = dev_get_priv(dev); | |
298 | struct atmel_hlcd_regs *regs = priv->regs; | |
299 | struct display_timing *timing = &priv->timing; | |
300 | struct lcd_dma_desc *desc; | |
301 | unsigned long value, vl_clk_pol; | |
302 | int ret; | |
303 | ||
304 | /* Disable DISP signal */ | |
305 | writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); | |
48263504 ÁFR |
306 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, |
307 | false, 1000, false); | |
7927831e SW |
308 | if (ret) |
309 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
310 | /* Disable synchronization */ | |
311 | writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); | |
48263504 ÁFR |
312 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, |
313 | false, 1000, false); | |
7927831e SW |
314 | if (ret) |
315 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
316 | /* Disable pixel clock */ | |
317 | writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); | |
48263504 ÁFR |
318 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, |
319 | false, 1000, false); | |
7927831e SW |
320 | if (ret) |
321 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
322 | /* Disable PWM */ | |
323 | writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); | |
48263504 ÁFR |
324 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, |
325 | false, 1000, false); | |
7927831e SW |
326 | if (ret) |
327 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
328 | ||
329 | /* Set pixel clock */ | |
330 | value = priv->clk_rate / timing->pixelclock.typ; | |
331 | if (priv->clk_rate % timing->pixelclock.typ) | |
332 | value++; | |
333 | ||
334 | vl_clk_pol = 0; | |
335 | if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) | |
336 | vl_clk_pol = LCDC_LCDCFG0_CLKPOL; | |
337 | ||
338 | if (value < 1) { | |
339 | /* Using system clock as pixel clock */ | |
340 | writel(LCDC_LCDCFG0_CLKDIV(0) | |
341 | | LCDC_LCDCFG0_CGDISHCR | |
342 | | LCDC_LCDCFG0_CGDISHEO | |
343 | | LCDC_LCDCFG0_CGDISOVR1 | |
344 | | LCDC_LCDCFG0_CGDISBASE | |
345 | | vl_clk_pol | |
346 | | LCDC_LCDCFG0_CLKSEL, | |
347 | ®s->lcdc_lcdcfg0); | |
348 | ||
349 | } else { | |
350 | writel(LCDC_LCDCFG0_CLKDIV(value - 2) | |
351 | | LCDC_LCDCFG0_CGDISHCR | |
352 | | LCDC_LCDCFG0_CGDISHEO | |
353 | | LCDC_LCDCFG0_CGDISOVR1 | |
354 | | LCDC_LCDCFG0_CGDISBASE | |
355 | | vl_clk_pol, | |
356 | ®s->lcdc_lcdcfg0); | |
357 | } | |
358 | ||
359 | /* Initialize control register 5 */ | |
360 | value = 0; | |
361 | ||
362 | if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH)) | |
363 | value |= LCDC_LCDCFG5_HSPOL; | |
364 | if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH)) | |
365 | value |= LCDC_LCDCFG5_VSPOL; | |
366 | ||
367 | switch (priv->output_mode) { | |
368 | case 12: | |
369 | value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP; | |
370 | break; | |
371 | case 16: | |
372 | value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP; | |
373 | break; | |
374 | case 18: | |
375 | value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP; | |
376 | break; | |
377 | case 24: | |
378 | value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; | |
379 | break; | |
380 | default: | |
381 | BUG(); | |
382 | break; | |
383 | } | |
384 | ||
385 | value |= LCDC_LCDCFG5_GUARDTIME(priv->guard_time); | |
386 | value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS); | |
387 | writel(value, ®s->lcdc_lcdcfg5); | |
388 | ||
389 | /* Vertical & Horizontal Timing */ | |
390 | value = LCDC_LCDCFG1_VSPW(timing->vsync_len.typ - 1); | |
391 | value |= LCDC_LCDCFG1_HSPW(timing->hsync_len.typ - 1); | |
392 | writel(value, ®s->lcdc_lcdcfg1); | |
393 | ||
394 | value = LCDC_LCDCFG2_VBPW(timing->vback_porch.typ); | |
395 | value |= LCDC_LCDCFG2_VFPW(timing->vfront_porch.typ - 1); | |
396 | writel(value, ®s->lcdc_lcdcfg2); | |
397 | ||
398 | value = LCDC_LCDCFG3_HBPW(timing->hback_porch.typ - 1); | |
399 | value |= LCDC_LCDCFG3_HFPW(timing->hfront_porch.typ - 1); | |
400 | writel(value, ®s->lcdc_lcdcfg3); | |
401 | ||
402 | /* Display size */ | |
403 | value = LCDC_LCDCFG4_RPF(timing->vactive.typ - 1); | |
404 | value |= LCDC_LCDCFG4_PPL(timing->hactive.typ - 1); | |
405 | writel(value, ®s->lcdc_lcdcfg4); | |
406 | ||
407 | writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO, | |
408 | ®s->lcdc_basecfg0); | |
409 | ||
410 | switch (VNBITS(priv->vl_bpix)) { | |
411 | case 16: | |
412 | writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565, | |
413 | ®s->lcdc_basecfg1); | |
414 | break; | |
415 | case 32: | |
416 | writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888, | |
417 | ®s->lcdc_basecfg1); | |
418 | break; | |
419 | default: | |
420 | BUG(); | |
421 | break; | |
422 | } | |
423 | ||
424 | writel(LCDC_BASECFG2_XSTRIDE(0), ®s->lcdc_basecfg2); | |
425 | writel(0, ®s->lcdc_basecfg3); | |
426 | writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4); | |
427 | ||
428 | /* Disable all interrupts */ | |
429 | writel(~0UL, ®s->lcdc_lcdidr); | |
430 | writel(~0UL, ®s->lcdc_baseidr); | |
431 | ||
432 | /* Setup the DMA descriptor, this descriptor will loop to itself */ | |
31e5c892 WY |
433 | desc = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*desc)); |
434 | if (!desc) | |
435 | return; | |
7927831e SW |
436 | |
437 | desc->address = (u32)uc_plat->base; | |
438 | ||
439 | /* Disable DMA transfer interrupt & descriptor loaded interrupt. */ | |
440 | desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN | |
441 | | LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH; | |
442 | desc->next = (u32)desc; | |
443 | ||
444 | /* Flush the DMA descriptor if we enabled dcache */ | |
31e5c892 WY |
445 | flush_dcache_range((u32)desc, |
446 | ALIGN(((u32)desc + sizeof(*desc)), | |
447 | CONFIG_SYS_CACHELINE_SIZE)); | |
7927831e SW |
448 | |
449 | writel(desc->address, ®s->lcdc_baseaddr); | |
450 | writel(desc->control, ®s->lcdc_basectrl); | |
451 | writel(desc->next, ®s->lcdc_basenext); | |
452 | writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN, | |
453 | ®s->lcdc_basecher); | |
454 | ||
455 | /* Enable LCD */ | |
456 | value = readl(®s->lcdc_lcden); | |
457 | writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
458 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, |
459 | true, 1000, false); | |
7927831e SW |
460 | if (ret) |
461 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
462 | value = readl(®s->lcdc_lcden); | |
463 | writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
464 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, |
465 | true, 1000, false); | |
7927831e SW |
466 | if (ret) |
467 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
468 | value = readl(®s->lcdc_lcden); | |
469 | writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
470 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, |
471 | true, 1000, false); | |
7927831e SW |
472 | if (ret) |
473 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
474 | value = readl(®s->lcdc_lcden); | |
475 | writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
476 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, |
477 | true, 1000, false); | |
7927831e SW |
478 | if (ret) |
479 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
480 | } | |
481 | ||
482 | static int atmel_hlcdc_probe(struct udevice *dev) | |
483 | { | |
484 | struct video_priv *uc_priv = dev_get_uclass_priv(dev); | |
485 | struct atmel_hlcdc_priv *priv = dev_get_priv(dev); | |
486 | int ret; | |
487 | ||
488 | ret = at91_hlcdc_enable_clk(dev); | |
489 | if (ret) | |
490 | return ret; | |
491 | ||
492 | atmel_hlcdc_init(dev); | |
493 | ||
494 | uc_priv->xsize = priv->timing.hactive.typ; | |
495 | uc_priv->ysize = priv->timing.vactive.typ; | |
496 | uc_priv->bpix = priv->vl_bpix; | |
497 | ||
498 | /* Enable flushing if we enabled dcache */ | |
499 | video_set_flush_dcache(dev, true); | |
500 | ||
501 | return 0; | |
502 | } | |
503 | ||
504 | static int atmel_hlcdc_ofdata_to_platdata(struct udevice *dev) | |
505 | { | |
506 | struct atmel_hlcdc_priv *priv = dev_get_priv(dev); | |
507 | const void *blob = gd->fdt_blob; | |
da409ccc | 508 | int node = dev_of_offset(dev); |
7927831e | 509 | |
7208396b | 510 | priv->regs = (struct atmel_hlcd_regs *)devfdt_get_addr(dev); |
7927831e SW |
511 | if (!priv->regs) { |
512 | debug("%s: No display controller address\n", __func__); | |
513 | return -EINVAL; | |
514 | } | |
515 | ||
da409ccc | 516 | if (fdtdec_decode_display_timing(blob, dev_of_offset(dev), |
7927831e SW |
517 | 0, &priv->timing)) { |
518 | debug("%s: Failed to decode display timing\n", __func__); | |
519 | return -EINVAL; | |
520 | } | |
521 | ||
522 | if (priv->timing.hactive.typ > LCD_MAX_WIDTH) | |
523 | priv->timing.hactive.typ = LCD_MAX_WIDTH; | |
524 | ||
525 | if (priv->timing.vactive.typ > LCD_MAX_HEIGHT) | |
526 | priv->timing.vactive.typ = LCD_MAX_HEIGHT; | |
527 | ||
528 | priv->vl_bpix = fdtdec_get_int(blob, node, "atmel,vl-bpix", 0); | |
529 | if (!priv->vl_bpix) { | |
530 | debug("%s: Failed to get bits per pixel\n", __func__); | |
531 | return -EINVAL; | |
532 | } | |
533 | ||
534 | priv->output_mode = fdtdec_get_int(blob, node, "atmel,output-mode", 24); | |
535 | priv->guard_time = fdtdec_get_int(blob, node, "atmel,guard-time", 1); | |
536 | ||
537 | return 0; | |
538 | } | |
539 | ||
540 | static int atmel_hlcdc_bind(struct udevice *dev) | |
541 | { | |
542 | struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); | |
543 | ||
544 | uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * | |
545 | (1 << LCD_MAX_LOG2_BPP) / 8; | |
546 | ||
547 | debug("%s: Frame buffer size %x\n", __func__, uc_plat->size); | |
548 | ||
549 | return 0; | |
550 | } | |
551 | ||
552 | static const struct udevice_id atmel_hlcdc_ids[] = { | |
553 | { .compatible = "atmel,sama5d2-hlcdc" }, | |
554 | { .compatible = "atmel,at91sam9x5-hlcdc" }, | |
555 | { } | |
556 | }; | |
557 | ||
558 | U_BOOT_DRIVER(atmel_hlcdfb) = { | |
559 | .name = "atmel_hlcdfb", | |
560 | .id = UCLASS_VIDEO, | |
561 | .of_match = atmel_hlcdc_ids, | |
562 | .bind = atmel_hlcdc_bind, | |
563 | .probe = atmel_hlcdc_probe, | |
564 | .ofdata_to_platdata = atmel_hlcdc_ofdata_to_platdata, | |
565 | .priv_auto_alloc_size = sizeof(struct atmel_hlcdc_priv), | |
566 | }; | |
567 | ||
568 | #endif |