blob: 9f4fc7f51524449ecf8e155550e0fb3b641870d9 [file] [log] [blame]
Miquel Raynal3a7c7e62025-04-03 09:39:12 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * i.MX8 LCD interface driver inspired from the Linux driver
4 * Copyright 2019 NXP
5 * Copyright 2024 Bootlin
6 * Adapted by Miquel Raynal <miquel.raynal@bootlin.com>
7 */
8
9#include <asm/io.h>
10#include <asm/mach-imx/dma.h>
11#include <clk.h>
12#include <dm.h>
13#include <panel.h>
14#include <power-domain.h>
15#include <video.h>
16#include <video_bridge.h>
17#include <linux/delay.h>
18
19#include "../videomodes.h"
20
21#define LCDIFV3_CTRL 0x0
22#define LCDIFV3_CTRL_SET 0x4
23#define LCDIFV3_CTRL_CLR 0x8
24#define CTRL_INV_HS BIT(0)
25#define CTRL_INV_VS BIT(1)
26#define CTRL_INV_DE BIT(2)
27#define CTRL_INV_PXCK BIT(3)
28#define CTRL_CLK_GATE BIT(30)
29#define CTRL_SW_RESET BIT(31)
30
31#define LCDIFV3_DISP_PARA 0x10
32#define DISP_PARA_DISP_MODE_NORMAL 0
33#define DISP_PARA_LINE_PATTERN_RGB_YUV 0
34#define DISP_PARA_DISP_ON BIT(31)
35
36#define LCDIFV3_DISP_SIZE 0x14
37#define DISP_SIZE_DELTA_X(x) ((x) & 0xffff)
38#define DISP_SIZE_DELTA_Y(x) ((x) << 16)
39
40#define LCDIFV3_HSYN_PARA 0x18
41#define HSYN_PARA_FP_H(x) ((x) & 0xffff)
42#define HSYN_PARA_BP_H(x) ((x) << 16)
43
44#define LCDIFV3_VSYN_PARA 0x1C
45#define VSYN_PARA_FP_V(x) ((x) & 0xffff)
46#define VSYN_PARA_BP_V(x) ((x) << 16)
47
48#define LCDIFV3_VSYN_HSYN_WIDTH 0x20
49#define VSYN_HSYN_PW_H(x) ((x) & 0xffff)
50#define VSYN_HSYN_PW_V(x) ((x) << 16)
51
52#define LCDIFV3_CTRLDESCL0_1 0x200
53#define CTRLDESCL0_1_WIDTH(x) ((x) & 0xffff)
54#define CTRLDESCL0_1_HEIGHT(x) ((x) << 16)
55
56#define LCDIFV3_CTRLDESCL0_3 0x208
57#define CTRLDESCL0_3_PITCH(x) ((x) & 0xFFFF)
58
59#define LCDIFV3_CTRLDESCL_LOW0_4 0x20C
60#define LCDIFV3_CTRLDESCL_HIGH0_4 0x210
61
62#define LCDIFV3_CTRLDESCL0_5 0x214
63#define CTRLDESCL0_5_YUV_FORMAT(x) (((x) & 0x3) << 14)
64#define CTRLDESCL0_5_BPP(x) (((x) & 0xf) << 24)
65#define BPP32_ARGB8888 0x9
66#define CTRLDESCL0_5_SHADOW_LOAD_EN BIT(30)
67#define CTRLDESCL0_5_EN BIT(31)
68
69struct lcdifv3_priv {
70 void __iomem *base;
71 struct clk pix_clk;
72 struct power_domain pd;
73 struct udevice *panel;
74 struct udevice *bridge;
75};
76
77static void lcdifv3_set_mode(struct lcdifv3_priv *priv,
78 struct display_timing *timings)
79{
80 u32 reg;
81
82 writel(DISP_SIZE_DELTA_X(timings->hactive.typ) |
83 DISP_SIZE_DELTA_Y(timings->vactive.typ),
84 priv->base + LCDIFV3_DISP_SIZE);
85
86 writel(HSYN_PARA_FP_H(timings->hfront_porch.typ) |
87 HSYN_PARA_BP_H(timings->hback_porch.typ),
88 priv->base + LCDIFV3_HSYN_PARA);
89
90 writel(VSYN_PARA_BP_V(timings->vback_porch.typ) |
91 VSYN_PARA_FP_V(timings->vfront_porch.typ),
92 priv->base + LCDIFV3_VSYN_PARA);
93
94 writel(VSYN_HSYN_PW_H(timings->hsync_len.typ) |
95 VSYN_HSYN_PW_V(timings->vsync_len.typ),
96 priv->base + LCDIFV3_VSYN_HSYN_WIDTH);
97
98 writel(CTRLDESCL0_1_WIDTH(timings->hactive.typ) |
99 CTRLDESCL0_1_HEIGHT(timings->vactive.typ),
100 priv->base + LCDIFV3_CTRLDESCL0_1);
101
102 if (timings->flags & DISPLAY_FLAGS_HSYNC_LOW)
103 writel(CTRL_INV_HS, priv->base + LCDIFV3_CTRL_SET);
104 else
105 writel(CTRL_INV_HS, priv->base + LCDIFV3_CTRL_CLR);
106
107 if (timings->flags & DISPLAY_FLAGS_VSYNC_LOW)
108 writel(CTRL_INV_VS, priv->base + LCDIFV3_CTRL_SET);
109 else
110 writel(CTRL_INV_VS, priv->base + LCDIFV3_CTRL_CLR);
111
112 if (timings->flags & DISPLAY_FLAGS_DE_LOW)
113 writel(CTRL_INV_DE, priv->base + LCDIFV3_CTRL_SET);
114 else
115 writel(CTRL_INV_DE, priv->base + LCDIFV3_CTRL_CLR);
116
117 if (timings->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
118 writel(CTRL_INV_PXCK, priv->base + LCDIFV3_CTRL_SET);
119 else
120 writel(CTRL_INV_PXCK, priv->base + LCDIFV3_CTRL_CLR);
121
122 writel(0, priv->base + LCDIFV3_DISP_PARA);
123
124 reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5);
125 reg &= ~(CTRLDESCL0_5_BPP(0xf) | CTRLDESCL0_5_YUV_FORMAT(0x3));
126 reg |= CTRLDESCL0_5_BPP(BPP32_ARGB8888);
127 writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5);
128}
129
130static void lcdifv3_enable_controller(struct lcdifv3_priv *priv)
131{
132 u32 reg;
133
134 reg = readl(priv->base + LCDIFV3_DISP_PARA);
135 reg |= DISP_PARA_DISP_ON;
136 writel(reg, priv->base + LCDIFV3_DISP_PARA);
137
138 reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5);
139 reg |= CTRLDESCL0_5_EN;
140 writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5);
141}
142
143static int lcdifv3_video_sync(struct udevice *dev)
144{
145 struct lcdifv3_priv *priv = dev_get_priv(dev);
146 u32 reg;
147
148 reg = readl(priv->base + LCDIFV3_CTRLDESCL0_5);
149 reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
150 writel(reg, priv->base + LCDIFV3_CTRLDESCL0_5);
151
152 return 0;
153}
154
155static void lcdifv3_init(struct udevice *dev, struct display_timing *timings)
156{
157 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
158 struct lcdifv3_priv *priv = dev_get_priv(dev);
159
160 clk_set_rate(&priv->pix_clk, timings->pixelclock.typ);
161
162 writel(CTRL_SW_RESET | CTRL_CLK_GATE, priv->base + LCDIFV3_CTRL_CLR);
163 udelay(10);
164
165 lcdifv3_set_mode(priv, timings);
166
167 writel(plat->base & 0xFFFFFFFF, priv->base + LCDIFV3_CTRLDESCL_LOW0_4);
168 writel(plat->base >> 32, priv->base + LCDIFV3_CTRLDESCL_HIGH0_4);
169
170 writel(CTRLDESCL0_3_PITCH(timings->hactive.typ * 4), /* 32bpp */
171 priv->base + LCDIFV3_CTRLDESCL0_3);
172
173 lcdifv3_enable_controller(priv);
174}
175
176static int lcdifv3_video_probe(struct udevice *dev)
177{
178 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
179 struct video_priv *uc_priv = dev_get_uclass_priv(dev);
180 struct lcdifv3_priv *priv = dev_get_priv(dev);
181 struct clk axi_clk, disp_axi_clk;
182 struct display_timing timings;
183 u32 fb_start, fb_end;
184 int ret;
185
186 ret = power_domain_get(dev, &priv->pd);
187 if (ret < 0)
188 return ret;
189
190 ret = clk_get_by_name(dev, "pix", &priv->pix_clk);
191 if (ret < 0)
192 return ret;
193
194 ret = clk_get_by_name(dev, "axi", &axi_clk);
195 if (ret < 0)
196 return ret;
197
198 ret = clk_get_by_name(dev, "disp_axi", &disp_axi_clk);
199 if (ret < 0)
200 return ret;
201
202 ret = power_domain_on(&priv->pd);
203 if (ret)
204 return ret;
205
206 ret = clk_enable(&priv->pix_clk);
207 if (ret)
208 goto dis_pd;
209
210 ret = clk_enable(&axi_clk);
211 if (ret)
212 goto dis_pix_clk;
213
214 ret = clk_enable(&disp_axi_clk);
215 if (ret)
216 goto dis_axi_clk;
217
218 priv->base = dev_remap_addr(dev);
219 if (!priv->base) {
220 ret = -EINVAL;
221 goto dis_clks;
222 }
223
224 /* Attach bridge */
225 ret = uclass_get_device_by_endpoint(UCLASS_VIDEO_BRIDGE, dev,
226 -1, -1, &priv->bridge);
227 if (ret)
228 goto dis_clks;
229
230 ret = video_bridge_attach(priv->bridge);
231 if (ret)
232 goto dis_clks;
233
234 ret = video_bridge_set_backlight(priv->bridge, 80);
235 if (ret)
236 goto dis_clks;
237
238 /* Attach panels */
239 ret = uclass_get_device_by_endpoint(UCLASS_PANEL, priv->bridge,
240 1, -1, &priv->panel);
241 if (ret) {
242 ret = uclass_get_device_by_endpoint(UCLASS_PANEL, priv->bridge,
243 2, -1, &priv->panel);
244 if (ret)
245 goto dis_clks;
246 }
247
248 ret = panel_get_display_timing(priv->panel, &timings);
249 if (ret) {
250 ret = ofnode_decode_display_timing(dev_ofnode(priv->panel),
251 0, &timings);
252 if (ret) {
253 printf("Cannot decode panel timings (%d)\n", ret);
254 goto dis_clks;
255 }
256 }
257
258 lcdifv3_init(dev, &timings);
259
260 /* Only support 32bpp for now */
261 uc_priv->bpix = VIDEO_BPP32;
262 uc_priv->xsize = timings.hactive.typ;
263 uc_priv->ysize = timings.vactive.typ;
264
265 /* Enable dcache for the frame buffer */
266 fb_start = plat->base & ~(MMU_SECTION_SIZE - 1);
267 fb_end = ALIGN(plat->base + plat->size, 1 << MMU_SECTION_SHIFT);
268 mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
269 DCACHE_WRITEBACK);
270 video_set_flush_dcache(dev, true);
271
272 return 0;
273
274dis_clks:
275 clk_disable(&disp_axi_clk);
276dis_axi_clk:
277 clk_disable(&axi_clk);
278dis_pix_clk:
279 clk_disable(&priv->pix_clk);
280dis_pd:
281 power_domain_off(&priv->pd);
282
283 return ret;
284}
285
286static int lcdifv3_video_bind(struct udevice *dev)
287{
288 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
289
290 /* Max size supported by LCDIF */
291 plat->size = 1920 * 1080 * VNBYTES(VIDEO_BPP32);
292
293 return 0;
294}
295
296static const struct udevice_id lcdifv3_video_ids[] = {
297 { .compatible = "fsl,imx8mp-lcdif" },
298 { }
299};
300
301static struct video_ops lcdifv3_video_ops = {
302 .video_sync = lcdifv3_video_sync,
303};
304
305U_BOOT_DRIVER(lcdifv3_video) = {
306 .name = "lcdif",
307 .id = UCLASS_VIDEO,
308 .of_match = lcdifv3_video_ids,
309 .bind = lcdifv3_video_bind,
310 .ops = &lcdifv3_video_ops,
311 .probe = lcdifv3_video_probe,
312 .priv_auto = sizeof(struct lcdifv3_priv),
313 .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
314};