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