]>
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 | ||
d678a59d | 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> |
401d1c4f | 13 | #include <asm/global_data.h> |
f6b690e6 BS |
14 | #include <asm/io.h> |
15 | #include <asm/arch/gpio.h> | |
16 | #include <asm/arch/clk.h> | |
7927831e SW |
17 | #include <clk.h> |
18 | #include <dm.h> | |
19 | #include <fdtdec.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 | |
7927831e SW |
25 | DECLARE_GLOBAL_DATA_PTR; |
26 | ||
7927831e SW |
27 | enum { |
28 | LCD_MAX_WIDTH = 1024, | |
29 | LCD_MAX_HEIGHT = 768, | |
30 | LCD_MAX_LOG2_BPP = VIDEO_BPP16, | |
31 | }; | |
32 | ||
33 | struct atmel_hlcdc_priv { | |
34 | struct atmel_hlcd_regs *regs; | |
35 | struct display_timing timing; | |
36 | unsigned int vl_bpix; | |
37 | unsigned int output_mode; | |
38 | unsigned int guard_time; | |
39 | ulong clk_rate; | |
40 | }; | |
41 | ||
42 | static int at91_hlcdc_enable_clk(struct udevice *dev) | |
43 | { | |
44 | struct atmel_hlcdc_priv *priv = dev_get_priv(dev); | |
45 | struct clk clk; | |
46 | ulong clk_rate; | |
47 | int ret; | |
48 | ||
49 | ret = clk_get_by_index(dev, 0, &clk); | |
50 | if (ret) | |
51 | return -EINVAL; | |
52 | ||
53 | ret = clk_enable(&clk); | |
54 | if (ret) | |
55 | return ret; | |
56 | ||
57 | clk_rate = clk_get_rate(&clk); | |
58 | if (!clk_rate) { | |
59 | clk_disable(&clk); | |
60 | return -ENODEV; | |
61 | } | |
62 | ||
63 | priv->clk_rate = clk_rate; | |
64 | ||
7927831e SW |
65 | return 0; |
66 | } | |
67 | ||
68 | static void atmel_hlcdc_init(struct udevice *dev) | |
69 | { | |
8a8d24bd | 70 | struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev); |
7927831e SW |
71 | struct atmel_hlcdc_priv *priv = dev_get_priv(dev); |
72 | struct atmel_hlcd_regs *regs = priv->regs; | |
73 | struct display_timing *timing = &priv->timing; | |
74 | struct lcd_dma_desc *desc; | |
75 | unsigned long value, vl_clk_pol; | |
76 | int ret; | |
77 | ||
78 | /* Disable DISP signal */ | |
79 | writel(LCDC_LCDDIS_DISPDIS, ®s->lcdc_lcddis); | |
48263504 ÁFR |
80 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, |
81 | false, 1000, false); | |
7927831e SW |
82 | if (ret) |
83 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
84 | /* Disable synchronization */ | |
85 | writel(LCDC_LCDDIS_SYNCDIS, ®s->lcdc_lcddis); | |
48263504 ÁFR |
86 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, |
87 | false, 1000, false); | |
7927831e SW |
88 | if (ret) |
89 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
90 | /* Disable pixel clock */ | |
91 | writel(LCDC_LCDDIS_CLKDIS, ®s->lcdc_lcddis); | |
48263504 ÁFR |
92 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, |
93 | false, 1000, false); | |
7927831e SW |
94 | if (ret) |
95 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
96 | /* Disable PWM */ | |
97 | writel(LCDC_LCDDIS_PWMDIS, ®s->lcdc_lcddis); | |
48263504 ÁFR |
98 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, |
99 | false, 1000, false); | |
7927831e SW |
100 | if (ret) |
101 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
102 | ||
103 | /* Set pixel clock */ | |
104 | value = priv->clk_rate / timing->pixelclock.typ; | |
105 | if (priv->clk_rate % timing->pixelclock.typ) | |
106 | value++; | |
107 | ||
108 | vl_clk_pol = 0; | |
109 | if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) | |
110 | vl_clk_pol = LCDC_LCDCFG0_CLKPOL; | |
111 | ||
112 | if (value < 1) { | |
113 | /* Using system clock as pixel clock */ | |
114 | writel(LCDC_LCDCFG0_CLKDIV(0) | |
115 | | LCDC_LCDCFG0_CGDISHCR | |
116 | | LCDC_LCDCFG0_CGDISHEO | |
117 | | LCDC_LCDCFG0_CGDISOVR1 | |
118 | | LCDC_LCDCFG0_CGDISBASE | |
119 | | vl_clk_pol | |
120 | | LCDC_LCDCFG0_CLKSEL, | |
121 | ®s->lcdc_lcdcfg0); | |
122 | ||
123 | } else { | |
124 | writel(LCDC_LCDCFG0_CLKDIV(value - 2) | |
125 | | LCDC_LCDCFG0_CGDISHCR | |
126 | | LCDC_LCDCFG0_CGDISHEO | |
127 | | LCDC_LCDCFG0_CGDISOVR1 | |
128 | | LCDC_LCDCFG0_CGDISBASE | |
129 | | vl_clk_pol, | |
130 | ®s->lcdc_lcdcfg0); | |
131 | } | |
132 | ||
133 | /* Initialize control register 5 */ | |
134 | value = 0; | |
135 | ||
136 | if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH)) | |
137 | value |= LCDC_LCDCFG5_HSPOL; | |
138 | if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH)) | |
139 | value |= LCDC_LCDCFG5_VSPOL; | |
140 | ||
141 | switch (priv->output_mode) { | |
142 | case 12: | |
143 | value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP; | |
144 | break; | |
145 | case 16: | |
146 | value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP; | |
147 | break; | |
148 | case 18: | |
149 | value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP; | |
150 | break; | |
151 | case 24: | |
152 | value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP; | |
153 | break; | |
154 | default: | |
155 | BUG(); | |
156 | break; | |
157 | } | |
158 | ||
159 | value |= LCDC_LCDCFG5_GUARDTIME(priv->guard_time); | |
160 | value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS); | |
161 | writel(value, ®s->lcdc_lcdcfg5); | |
162 | ||
163 | /* Vertical & Horizontal Timing */ | |
164 | value = LCDC_LCDCFG1_VSPW(timing->vsync_len.typ - 1); | |
165 | value |= LCDC_LCDCFG1_HSPW(timing->hsync_len.typ - 1); | |
166 | writel(value, ®s->lcdc_lcdcfg1); | |
167 | ||
168 | value = LCDC_LCDCFG2_VBPW(timing->vback_porch.typ); | |
169 | value |= LCDC_LCDCFG2_VFPW(timing->vfront_porch.typ - 1); | |
170 | writel(value, ®s->lcdc_lcdcfg2); | |
171 | ||
172 | value = LCDC_LCDCFG3_HBPW(timing->hback_porch.typ - 1); | |
173 | value |= LCDC_LCDCFG3_HFPW(timing->hfront_porch.typ - 1); | |
174 | writel(value, ®s->lcdc_lcdcfg3); | |
175 | ||
176 | /* Display size */ | |
177 | value = LCDC_LCDCFG4_RPF(timing->vactive.typ - 1); | |
178 | value |= LCDC_LCDCFG4_PPL(timing->hactive.typ - 1); | |
179 | writel(value, ®s->lcdc_lcdcfg4); | |
180 | ||
181 | writel(LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO, | |
182 | ®s->lcdc_basecfg0); | |
183 | ||
184 | switch (VNBITS(priv->vl_bpix)) { | |
185 | case 16: | |
186 | writel(LCDC_BASECFG1_RGBMODE_16BPP_RGB_565, | |
187 | ®s->lcdc_basecfg1); | |
188 | break; | |
189 | case 32: | |
190 | writel(LCDC_BASECFG1_RGBMODE_24BPP_RGB_888, | |
191 | ®s->lcdc_basecfg1); | |
192 | break; | |
193 | default: | |
194 | BUG(); | |
195 | break; | |
196 | } | |
197 | ||
198 | writel(LCDC_BASECFG2_XSTRIDE(0), ®s->lcdc_basecfg2); | |
199 | writel(0, ®s->lcdc_basecfg3); | |
200 | writel(LCDC_BASECFG4_DMA, ®s->lcdc_basecfg4); | |
201 | ||
202 | /* Disable all interrupts */ | |
203 | writel(~0UL, ®s->lcdc_lcdidr); | |
204 | writel(~0UL, ®s->lcdc_baseidr); | |
205 | ||
206 | /* Setup the DMA descriptor, this descriptor will loop to itself */ | |
31e5c892 WY |
207 | desc = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*desc)); |
208 | if (!desc) | |
209 | return; | |
7927831e SW |
210 | |
211 | desc->address = (u32)uc_plat->base; | |
212 | ||
213 | /* Disable DMA transfer interrupt & descriptor loaded interrupt. */ | |
214 | desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN | |
215 | | LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH; | |
216 | desc->next = (u32)desc; | |
217 | ||
218 | /* Flush the DMA descriptor if we enabled dcache */ | |
31e5c892 WY |
219 | flush_dcache_range((u32)desc, |
220 | ALIGN(((u32)desc + sizeof(*desc)), | |
221 | CONFIG_SYS_CACHELINE_SIZE)); | |
7927831e SW |
222 | |
223 | writel(desc->address, ®s->lcdc_baseaddr); | |
224 | writel(desc->control, ®s->lcdc_basectrl); | |
225 | writel(desc->next, ®s->lcdc_basenext); | |
226 | writel(LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN, | |
227 | ®s->lcdc_basecher); | |
228 | ||
229 | /* Enable LCD */ | |
230 | value = readl(®s->lcdc_lcden); | |
231 | writel(value | LCDC_LCDEN_CLKEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
232 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_CLKSTS, |
233 | true, 1000, false); | |
7927831e SW |
234 | if (ret) |
235 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
236 | value = readl(®s->lcdc_lcden); | |
237 | writel(value | LCDC_LCDEN_SYNCEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
238 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_LCDSTS, |
239 | true, 1000, false); | |
7927831e SW |
240 | if (ret) |
241 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
242 | value = readl(®s->lcdc_lcden); | |
243 | writel(value | LCDC_LCDEN_DISPEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
244 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_DISPSTS, |
245 | true, 1000, false); | |
7927831e SW |
246 | if (ret) |
247 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
248 | value = readl(®s->lcdc_lcden); | |
249 | writel(value | LCDC_LCDEN_PWMEN, ®s->lcdc_lcden); | |
48263504 ÁFR |
250 | ret = wait_for_bit_le32(®s->lcdc_lcdsr, LCDC_LCDSR_PWMSTS, |
251 | true, 1000, false); | |
7927831e SW |
252 | if (ret) |
253 | printf("%s: %d: Timeout!\n", __func__, __LINE__); | |
254 | } | |
255 | ||
256 | static int atmel_hlcdc_probe(struct udevice *dev) | |
257 | { | |
258 | struct video_priv *uc_priv = dev_get_uclass_priv(dev); | |
259 | struct atmel_hlcdc_priv *priv = dev_get_priv(dev); | |
260 | int ret; | |
261 | ||
262 | ret = at91_hlcdc_enable_clk(dev); | |
263 | if (ret) | |
264 | return ret; | |
265 | ||
266 | atmel_hlcdc_init(dev); | |
267 | ||
268 | uc_priv->xsize = priv->timing.hactive.typ; | |
269 | uc_priv->ysize = priv->timing.vactive.typ; | |
270 | uc_priv->bpix = priv->vl_bpix; | |
271 | ||
272 | /* Enable flushing if we enabled dcache */ | |
273 | video_set_flush_dcache(dev, true); | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
d1998a9f | 278 | static int atmel_hlcdc_of_to_plat(struct udevice *dev) |
7927831e SW |
279 | { |
280 | struct atmel_hlcdc_priv *priv = dev_get_priv(dev); | |
281 | const void *blob = gd->fdt_blob; | |
da409ccc | 282 | int node = dev_of_offset(dev); |
7927831e | 283 | |
8613c8d8 | 284 | priv->regs = dev_read_addr_ptr(dev); |
7927831e SW |
285 | if (!priv->regs) { |
286 | debug("%s: No display controller address\n", __func__); | |
287 | return -EINVAL; | |
288 | } | |
289 | ||
da409ccc | 290 | if (fdtdec_decode_display_timing(blob, dev_of_offset(dev), |
7927831e SW |
291 | 0, &priv->timing)) { |
292 | debug("%s: Failed to decode display timing\n", __func__); | |
293 | return -EINVAL; | |
294 | } | |
295 | ||
296 | if (priv->timing.hactive.typ > LCD_MAX_WIDTH) | |
297 | priv->timing.hactive.typ = LCD_MAX_WIDTH; | |
298 | ||
299 | if (priv->timing.vactive.typ > LCD_MAX_HEIGHT) | |
300 | priv->timing.vactive.typ = LCD_MAX_HEIGHT; | |
301 | ||
302 | priv->vl_bpix = fdtdec_get_int(blob, node, "atmel,vl-bpix", 0); | |
303 | if (!priv->vl_bpix) { | |
304 | debug("%s: Failed to get bits per pixel\n", __func__); | |
305 | return -EINVAL; | |
306 | } | |
307 | ||
308 | priv->output_mode = fdtdec_get_int(blob, node, "atmel,output-mode", 24); | |
309 | priv->guard_time = fdtdec_get_int(blob, node, "atmel,guard-time", 1); | |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
314 | static int atmel_hlcdc_bind(struct udevice *dev) | |
315 | { | |
8a8d24bd | 316 | struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev); |
7927831e SW |
317 | |
318 | uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * | |
319 | (1 << LCD_MAX_LOG2_BPP) / 8; | |
320 | ||
321 | debug("%s: Frame buffer size %x\n", __func__, uc_plat->size); | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | static const struct udevice_id atmel_hlcdc_ids[] = { | |
327 | { .compatible = "atmel,sama5d2-hlcdc" }, | |
328 | { .compatible = "atmel,at91sam9x5-hlcdc" }, | |
329 | { } | |
330 | }; | |
331 | ||
332 | U_BOOT_DRIVER(atmel_hlcdfb) = { | |
333 | .name = "atmel_hlcdfb", | |
334 | .id = UCLASS_VIDEO, | |
335 | .of_match = atmel_hlcdc_ids, | |
336 | .bind = atmel_hlcdc_bind, | |
337 | .probe = atmel_hlcdc_probe, | |
d1998a9f | 338 | .of_to_plat = atmel_hlcdc_of_to_plat, |
41575d8e | 339 | .priv_auto = sizeof(struct atmel_hlcdc_priv), |
7927831e | 340 | }; |