video: atmel: Drop pre-DM parts of video driver
[platform/kernel/u-boot.git] / drivers / video / atmel_lcdfb.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Driver for AT91/AT32 LCD Controller
4  *
5  * Copyright (C) 2007 Atmel Corporation
6  */
7
8 #include <common.h>
9 #include <atmel_lcd.h>
10 #include <dm.h>
11 #include <fdtdec.h>
12 #include <log.h>
13 #include <part.h>
14 #include <video.h>
15 #include <asm/global_data.h>
16 #include <asm/io.h>
17 #include <asm/arch/gpio.h>
18 #include <asm/arch/clk.h>
19 #include <lcd.h>
20 #include <bmp_layout.h>
21 #include <atmel_lcdc.h>
22 #include <linux/delay.h>
23
24 DECLARE_GLOBAL_DATA_PTR;
25
26 enum {
27         /* Maximum LCD size we support */
28         LCD_MAX_WIDTH           = 1366,
29         LCD_MAX_HEIGHT          = 768,
30         LCD_MAX_LOG2_BPP        = VIDEO_BPP16,
31 };
32
33 struct atmel_fb_priv {
34         struct display_timing timing;
35 };
36
37 /* configurable parameters */
38 #define ATMEL_LCDC_CVAL_DEFAULT         0xc8
39 #define ATMEL_LCDC_DMA_BURST_LEN        8
40 #ifndef ATMEL_LCDC_GUARD_TIME
41 #define ATMEL_LCDC_GUARD_TIME           1
42 #endif
43
44 #if defined(CONFIG_AT91SAM9263)
45 #define ATMEL_LCDC_FIFO_SIZE            2048
46 #else
47 #define ATMEL_LCDC_FIFO_SIZE            512
48 #endif
49
50 #define lcdc_readl(mmio, reg)           __raw_readl((mmio)+(reg))
51 #define lcdc_writel(mmio, reg, val)     __raw_writel((val), (mmio)+(reg))
52
53 static void atmel_fb_init(ulong addr, struct display_timing *timing, int bpix,
54                           bool tft, bool cont_pol_low, ulong lcdbase)
55 {
56         unsigned long value;
57         void *reg = (void *)addr;
58
59         /* Turn off the LCD controller and the DMA controller */
60         lcdc_writel(reg, ATMEL_LCDC_PWRCON,
61                     ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET);
62
63         /* Wait for the LCDC core to become idle */
64         while (lcdc_readl(reg, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
65                 udelay(10);
66
67         lcdc_writel(reg, ATMEL_LCDC_DMACON, 0);
68
69         /* Reset LCDC DMA */
70         lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST);
71
72         /* ...set frame size and burst length = 8 words (?) */
73         value = (timing->hactive.typ * timing->vactive.typ *
74                  (1 << bpix)) / 32;
75         value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET);
76         lcdc_writel(reg, ATMEL_LCDC_DMAFRMCFG, value);
77
78         /* Set pixel clock */
79         value = get_lcdc_clk_rate(0) / timing->pixelclock.typ;
80         if (get_lcdc_clk_rate(0) % timing->pixelclock.typ)
81                 value++;
82         value = (value / 2) - 1;
83
84         if (!value) {
85                 lcdc_writel(reg, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS);
86         } else
87                 lcdc_writel(reg, ATMEL_LCDC_LCDCON1,
88                             value << ATMEL_LCDC_CLKVAL_OFFSET);
89
90         /* Initialize control register 2 */
91         value = ATMEL_LCDC_MEMOR_LITTLE | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE;
92         if (tft)
93                 value |= ATMEL_LCDC_DISTYPE_TFT;
94
95         if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
96                 value |= ATMEL_LCDC_INVLINE_INVERTED;
97         if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
98                 value |= ATMEL_LCDC_INVFRAME_INVERTED;
99         value |= bpix << 5;
100         lcdc_writel(reg, ATMEL_LCDC_LCDCON2, value);
101
102         /* Vertical timing */
103         value = (timing->vsync_len.typ - 1) << ATMEL_LCDC_VPW_OFFSET;
104         value |= timing->vback_porch.typ << ATMEL_LCDC_VBP_OFFSET;
105         value |= timing->vfront_porch.typ;
106         /* Magic! (Datasheet says "Bit 31 must be written to 1") */
107         value |= 1U << 31;
108         lcdc_writel(reg, ATMEL_LCDC_TIM1, value);
109
110         /* Horizontal timing */
111         value = (timing->hfront_porch.typ - 1) << ATMEL_LCDC_HFP_OFFSET;
112         value |= (timing->hsync_len.typ - 1) << ATMEL_LCDC_HPW_OFFSET;
113         value |= (timing->hback_porch.typ - 1);
114         lcdc_writel(reg, ATMEL_LCDC_TIM2, value);
115
116         /* Display size */
117         value = (timing->hactive.typ - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
118         value |= timing->vactive.typ - 1;
119         lcdc_writel(reg, ATMEL_LCDC_LCDFRMCFG, value);
120
121         /* FIFO Threshold: Use formula from data sheet */
122         value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3);
123         lcdc_writel(reg, ATMEL_LCDC_FIFO, value);
124
125         /* Toggle LCD_MODE every frame */
126         lcdc_writel(reg, ATMEL_LCDC_MVAL, 0);
127
128         /* Disable all interrupts */
129         lcdc_writel(reg, ATMEL_LCDC_IDR, ~0UL);
130
131         /* Set contrast */
132         value = ATMEL_LCDC_PS_DIV8 |
133                 ATMEL_LCDC_ENA_PWMENABLE;
134         if (!cont_pol_low)
135                 value |= ATMEL_LCDC_POL_POSITIVE;
136         lcdc_writel(reg, ATMEL_LCDC_CONTRAST_CTR, value);
137         lcdc_writel(reg, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
138
139         /* Set framebuffer DMA base address and pixel offset */
140         lcdc_writel(reg, ATMEL_LCDC_DMABADDR1, lcdbase);
141
142         lcdc_writel(reg, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMAEN);
143         lcdc_writel(reg, ATMEL_LCDC_PWRCON,
144                     (ATMEL_LCDC_GUARD_TIME << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
145 }
146
147 static int atmel_fb_lcd_probe(struct udevice *dev)
148 {
149         struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
150         struct video_priv *uc_priv = dev_get_uclass_priv(dev);
151         struct atmel_fb_priv *priv = dev_get_priv(dev);
152         struct display_timing *timing = &priv->timing;
153
154         /*
155          * For now some values are hard-coded. We could use the device tree
156          * bindings in simple-framebuffer.txt to specify the format/bpp and
157          * some Atmel-specific binding for tft and cont_pol_low.
158          */
159         atmel_fb_init(ATMEL_BASE_LCDC, timing, VIDEO_BPP16, true, false,
160                       uc_plat->base);
161         uc_priv->xsize = timing->hactive.typ;
162         uc_priv->ysize = timing->vactive.typ;
163         uc_priv->bpix = VIDEO_BPP16;
164         video_set_flush_dcache(dev, true);
165         debug("LCD frame buffer at %lx, size %x, %dx%d pixels\n", uc_plat->base,
166               uc_plat->size, uc_priv->xsize, uc_priv->ysize);
167
168         return 0;
169 }
170
171 static int atmel_fb_of_to_plat(struct udevice *dev)
172 {
173         struct atmel_lcd_plat *plat = dev_get_plat(dev);
174         struct atmel_fb_priv *priv = dev_get_priv(dev);
175         struct display_timing *timing = &priv->timing;
176         const void *blob = gd->fdt_blob;
177
178         if (fdtdec_decode_display_timing(blob, dev_of_offset(dev),
179                                          plat->timing_index, timing)) {
180                 debug("%s: Failed to decode display timing\n", __func__);
181                 return -EINVAL;
182         }
183
184         return 0;
185 }
186
187 static int atmel_fb_lcd_bind(struct udevice *dev)
188 {
189         struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
190
191         uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
192                         (1 << VIDEO_BPP16) / 8;
193         debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
194
195         return 0;
196 }
197
198 static const struct udevice_id atmel_fb_lcd_ids[] = {
199         { .compatible = "atmel,at91sam9g45-lcdc" },
200         { }
201 };
202
203 U_BOOT_DRIVER(atmel_fb) = {
204         .name   = "atmel_fb",
205         .id     = UCLASS_VIDEO,
206         .of_match = atmel_fb_lcd_ids,
207         .bind   = atmel_fb_lcd_bind,
208         .of_to_plat     = atmel_fb_of_to_plat,
209         .probe  = atmel_fb_lcd_probe,
210         .plat_auto      = sizeof(struct atmel_lcd_plat),
211         .priv_auto      = sizeof(struct atmel_fb_priv),
212 };