1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2022 Marek Vasut <marex@denx.de> |
4 | * |
5 | * This code is based on drivers/gpu/drm/mxsfb/mxsfb* |
6 | */ |
7 | |
8 | #include <linux/bitfield.h> |
9 | #include <linux/clk.h> |
10 | #include <linux/io.h> |
11 | #include <linux/iopoll.h> |
12 | #include <linux/media-bus-format.h> |
13 | #include <linux/pm_runtime.h> |
14 | #include <linux/spinlock.h> |
15 | |
16 | #include <drm/drm_atomic.h> |
17 | #include <drm/drm_atomic_helper.h> |
18 | #include <drm/drm_bridge.h> |
19 | #include <drm/drm_color_mgmt.h> |
20 | #include <drm/drm_connector.h> |
21 | #include <drm/drm_crtc.h> |
22 | #include <drm/drm_encoder.h> |
23 | #include <drm/drm_fb_dma_helper.h> |
24 | #include <drm/drm_fourcc.h> |
25 | #include <drm/drm_framebuffer.h> |
26 | #include <drm/drm_gem_atomic_helper.h> |
27 | #include <drm/drm_gem_dma_helper.h> |
28 | #include <drm/drm_plane.h> |
29 | #include <drm/drm_vblank.h> |
30 | |
31 | #include "lcdif_drv.h" |
32 | #include "lcdif_regs.h" |
33 | |
34 | struct lcdif_crtc_state { |
35 | struct drm_crtc_state base; /* always be the first member */ |
36 | u32 bus_format; |
37 | u32 bus_flags; |
38 | }; |
39 | |
40 | static inline struct lcdif_crtc_state * |
41 | to_lcdif_crtc_state(struct drm_crtc_state *s) |
42 | { |
43 | return container_of(s, struct lcdif_crtc_state, base); |
44 | } |
45 | |
46 | /* ----------------------------------------------------------------------------- |
47 | * CRTC |
48 | */ |
49 | |
50 | /* |
51 | * For conversion from YCbCr to RGB, the CSC operates as follows: |
52 | * |
53 | * |R| |A1 A2 A3| |Y + D1| |
54 | * |G| = |B1 B2 B3| * |Cb + D2| |
55 | * |B| |C1 C2 C3| |Cr + D3| |
56 | * |
57 | * The A, B and C coefficients are expressed as Q2.8 fixed point values, and |
58 | * the D coefficients as Q0.8. Despite the reference manual stating the |
59 | * opposite, the D1, D2 and D3 offset values are added to Y, Cb and Cr, not |
60 | * subtracted. They must thus be programmed with negative values. |
61 | */ |
62 | static const u32 lcdif_yuv2rgb_coeffs[3][2][6] = { |
63 | [DRM_COLOR_YCBCR_BT601] = { |
64 | [DRM_COLOR_YCBCR_LIMITED_RANGE] = { |
65 | /* |
66 | * BT.601 limited range: |
67 | * |
68 | * |R| |1.1644 0.0000 1.5960| |Y - 16 | |
69 | * |G| = |1.1644 -0.3917 -0.8129| * |Cb - 128| |
70 | * |B| |1.1644 2.0172 0.0000| |Cr - 128| |
71 | */ |
72 | CSC0_COEF0_A1(0x12a) | CSC0_COEF0_A2(0x000), |
73 | CSC0_COEF1_A3(0x199) | CSC0_COEF1_B1(0x12a), |
74 | CSC0_COEF2_B2(0x79c) | CSC0_COEF2_B3(0x730), |
75 | CSC0_COEF3_C1(0x12a) | CSC0_COEF3_C2(0x204), |
76 | CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x1f0), |
77 | CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180), |
78 | }, |
79 | [DRM_COLOR_YCBCR_FULL_RANGE] = { |
80 | /* |
81 | * BT.601 full range: |
82 | * |
83 | * |R| |1.0000 0.0000 1.4020| |Y - 0 | |
84 | * |G| = |1.0000 -0.3441 -0.7141| * |Cb - 128| |
85 | * |B| |1.0000 1.7720 0.0000| |Cr - 128| |
86 | */ |
87 | CSC0_COEF0_A1(0x100) | CSC0_COEF0_A2(0x000), |
88 | CSC0_COEF1_A3(0x167) | CSC0_COEF1_B1(0x100), |
89 | CSC0_COEF2_B2(0x7a8) | CSC0_COEF2_B3(0x749), |
90 | CSC0_COEF3_C1(0x100) | CSC0_COEF3_C2(0x1c6), |
91 | CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x000), |
92 | CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180), |
93 | }, |
94 | }, |
95 | [DRM_COLOR_YCBCR_BT709] = { |
96 | [DRM_COLOR_YCBCR_LIMITED_RANGE] = { |
97 | /* |
98 | * Rec.709 limited range: |
99 | * |
100 | * |R| |1.1644 0.0000 1.7927| |Y - 16 | |
101 | * |G| = |1.1644 -0.2132 -0.5329| * |Cb - 128| |
102 | * |B| |1.1644 2.1124 0.0000| |Cr - 128| |
103 | */ |
104 | CSC0_COEF0_A1(0x12a) | CSC0_COEF0_A2(0x000), |
105 | CSC0_COEF1_A3(0x1cb) | CSC0_COEF1_B1(0x12a), |
106 | CSC0_COEF2_B2(0x7c9) | CSC0_COEF2_B3(0x778), |
107 | CSC0_COEF3_C1(0x12a) | CSC0_COEF3_C2(0x21d), |
108 | CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x1f0), |
109 | CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180), |
110 | }, |
111 | [DRM_COLOR_YCBCR_FULL_RANGE] = { |
112 | /* |
113 | * Rec.709 full range: |
114 | * |
115 | * |R| |1.0000 0.0000 1.5748| |Y - 0 | |
116 | * |G| = |1.0000 -0.1873 -0.4681| * |Cb - 128| |
117 | * |B| |1.0000 1.8556 0.0000| |Cr - 128| |
118 | */ |
119 | CSC0_COEF0_A1(0x100) | CSC0_COEF0_A2(0x000), |
120 | CSC0_COEF1_A3(0x193) | CSC0_COEF1_B1(0x100), |
121 | CSC0_COEF2_B2(0x7d0) | CSC0_COEF2_B3(0x788), |
122 | CSC0_COEF3_C1(0x100) | CSC0_COEF3_C2(0x1db), |
123 | CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x000), |
124 | CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180), |
125 | }, |
126 | }, |
127 | [DRM_COLOR_YCBCR_BT2020] = { |
128 | [DRM_COLOR_YCBCR_LIMITED_RANGE] = { |
129 | /* |
130 | * BT.2020 limited range: |
131 | * |
132 | * |R| |1.1644 0.0000 1.6787| |Y - 16 | |
133 | * |G| = |1.1644 -0.1874 -0.6505| * |Cb - 128| |
134 | * |B| |1.1644 2.1418 0.0000| |Cr - 128| |
135 | */ |
136 | CSC0_COEF0_A1(0x12a) | CSC0_COEF0_A2(0x000), |
137 | CSC0_COEF1_A3(0x1ae) | CSC0_COEF1_B1(0x12a), |
138 | CSC0_COEF2_B2(0x7d0) | CSC0_COEF2_B3(0x759), |
139 | CSC0_COEF3_C1(0x12a) | CSC0_COEF3_C2(0x224), |
140 | CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x1f0), |
141 | CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180), |
142 | }, |
143 | [DRM_COLOR_YCBCR_FULL_RANGE] = { |
144 | /* |
145 | * BT.2020 full range: |
146 | * |
147 | * |R| |1.0000 0.0000 1.4746| |Y - 0 | |
148 | * |G| = |1.0000 -0.1646 -0.5714| * |Cb - 128| |
149 | * |B| |1.0000 1.8814 0.0000| |Cr - 128| |
150 | */ |
151 | CSC0_COEF0_A1(0x100) | CSC0_COEF0_A2(0x000), |
152 | CSC0_COEF1_A3(0x179) | CSC0_COEF1_B1(0x100), |
153 | CSC0_COEF2_B2(0x7d6) | CSC0_COEF2_B3(0x76e), |
154 | CSC0_COEF3_C1(0x100) | CSC0_COEF3_C2(0x1e2), |
155 | CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x000), |
156 | CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180), |
157 | }, |
158 | }, |
159 | }; |
160 | |
161 | static void lcdif_set_formats(struct lcdif_drm_private *lcdif, |
162 | struct drm_plane_state *plane_state, |
163 | const u32 bus_format) |
164 | { |
165 | struct drm_device *drm = lcdif->drm; |
166 | const u32 format = plane_state->fb->format->format; |
167 | bool in_yuv = false; |
168 | bool out_yuv = false; |
169 | |
170 | switch (bus_format) { |
171 | case MEDIA_BUS_FMT_RGB565_1X16: |
172 | writel(DISP_PARA_LINE_PATTERN_RGB565, |
173 | addr: lcdif->base + LCDC_V8_DISP_PARA); |
174 | break; |
175 | case MEDIA_BUS_FMT_RGB888_1X24: |
176 | writel(DISP_PARA_LINE_PATTERN_RGB888, |
177 | addr: lcdif->base + LCDC_V8_DISP_PARA); |
178 | break; |
179 | case MEDIA_BUS_FMT_UYVY8_1X16: |
180 | writel(DISP_PARA_LINE_PATTERN_UYVY_H, |
181 | addr: lcdif->base + LCDC_V8_DISP_PARA); |
182 | out_yuv = true; |
183 | break; |
184 | default: |
185 | dev_err(drm->dev, "Unknown media bus format 0x%x\n" , bus_format); |
186 | break; |
187 | } |
188 | |
189 | switch (format) { |
190 | /* RGB Formats */ |
191 | case DRM_FORMAT_RGB565: |
192 | writel(CTRLDESCL0_5_BPP_16_RGB565, |
193 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
194 | break; |
195 | case DRM_FORMAT_RGB888: |
196 | writel(CTRLDESCL0_5_BPP_24_RGB888, |
197 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
198 | break; |
199 | case DRM_FORMAT_XRGB1555: |
200 | writel(CTRLDESCL0_5_BPP_16_ARGB1555, |
201 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
202 | break; |
203 | case DRM_FORMAT_XRGB4444: |
204 | writel(CTRLDESCL0_5_BPP_16_ARGB4444, |
205 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
206 | break; |
207 | case DRM_FORMAT_XBGR8888: |
208 | writel(CTRLDESCL0_5_BPP_32_ABGR8888, |
209 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
210 | break; |
211 | case DRM_FORMAT_XRGB8888: |
212 | writel(CTRLDESCL0_5_BPP_32_ARGB8888, |
213 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
214 | break; |
215 | |
216 | /* YUV Formats */ |
217 | case DRM_FORMAT_YUYV: |
218 | writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_VY2UY1, |
219 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
220 | in_yuv = true; |
221 | break; |
222 | case DRM_FORMAT_YVYU: |
223 | writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_UY2VY1, |
224 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
225 | in_yuv = true; |
226 | break; |
227 | case DRM_FORMAT_UYVY: |
228 | writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_Y2VY1U, |
229 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
230 | in_yuv = true; |
231 | break; |
232 | case DRM_FORMAT_VYUY: |
233 | writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_Y2UY1V, |
234 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
235 | in_yuv = true; |
236 | break; |
237 | |
238 | default: |
239 | dev_err(drm->dev, "Unknown pixel format 0x%x\n" , format); |
240 | break; |
241 | } |
242 | |
243 | /* |
244 | * The CSC differentiates between "YCbCr" and "YUV", but the reference |
245 | * manual doesn't detail how they differ. Experiments showed that the |
246 | * luminance value is unaffected, only the calculations involving chroma |
247 | * values differ. The YCbCr mode behaves as expected, with chroma values |
248 | * being offset by 128. The YUV mode isn't fully understood. |
249 | */ |
250 | if (!in_yuv && out_yuv) { |
251 | /* RGB -> YCbCr */ |
252 | writel(CSC0_CTRL_CSC_MODE_RGB2YCbCr, |
253 | addr: lcdif->base + LCDC_V8_CSC0_CTRL); |
254 | |
255 | /* |
256 | * CSC: BT.601 Limited Range RGB to YCbCr coefficients. |
257 | * |
258 | * |Y | | 0.2568 0.5041 0.0979| |R| |16 | |
259 | * |Cb| = |-0.1482 -0.2910 0.4392| * |G| + |128| |
260 | * |Cr| | 0.4392 0.4392 -0.3678| |B| |128| |
261 | */ |
262 | writel(CSC0_COEF0_A2(0x081) | CSC0_COEF0_A1(0x041), |
263 | addr: lcdif->base + LCDC_V8_CSC0_COEF0); |
264 | writel(CSC0_COEF1_B1(0x7db) | CSC0_COEF1_A3(0x019), |
265 | addr: lcdif->base + LCDC_V8_CSC0_COEF1); |
266 | writel(CSC0_COEF2_B3(0x070) | CSC0_COEF2_B2(0x7b6), |
267 | addr: lcdif->base + LCDC_V8_CSC0_COEF2); |
268 | writel(CSC0_COEF3_C2(0x7a2) | CSC0_COEF3_C1(0x070), |
269 | addr: lcdif->base + LCDC_V8_CSC0_COEF3); |
270 | writel(CSC0_COEF4_D1(0x010) | CSC0_COEF4_C3(0x7ee), |
271 | addr: lcdif->base + LCDC_V8_CSC0_COEF4); |
272 | writel(CSC0_COEF5_D3(0x080) | CSC0_COEF5_D2(0x080), |
273 | addr: lcdif->base + LCDC_V8_CSC0_COEF5); |
274 | } else if (in_yuv && !out_yuv) { |
275 | /* YCbCr -> RGB */ |
276 | const u32 *coeffs = |
277 | lcdif_yuv2rgb_coeffs[plane_state->color_encoding] |
278 | [plane_state->color_range]; |
279 | |
280 | writel(CSC0_CTRL_CSC_MODE_YCbCr2RGB, |
281 | addr: lcdif->base + LCDC_V8_CSC0_CTRL); |
282 | |
283 | writel(val: coeffs[0], addr: lcdif->base + LCDC_V8_CSC0_COEF0); |
284 | writel(val: coeffs[1], addr: lcdif->base + LCDC_V8_CSC0_COEF1); |
285 | writel(val: coeffs[2], addr: lcdif->base + LCDC_V8_CSC0_COEF2); |
286 | writel(val: coeffs[3], addr: lcdif->base + LCDC_V8_CSC0_COEF3); |
287 | writel(val: coeffs[4], addr: lcdif->base + LCDC_V8_CSC0_COEF4); |
288 | writel(val: coeffs[5], addr: lcdif->base + LCDC_V8_CSC0_COEF5); |
289 | } else { |
290 | /* RGB -> RGB, YCbCr -> YCbCr: bypass colorspace converter. */ |
291 | writel(CSC0_CTRL_BYPASS, addr: lcdif->base + LCDC_V8_CSC0_CTRL); |
292 | } |
293 | } |
294 | |
295 | static void lcdif_set_mode(struct lcdif_drm_private *lcdif, u32 bus_flags) |
296 | { |
297 | struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode; |
298 | u32 ctrl = 0; |
299 | |
300 | if (m->flags & DRM_MODE_FLAG_NHSYNC) |
301 | ctrl |= CTRL_INV_HS; |
302 | if (m->flags & DRM_MODE_FLAG_NVSYNC) |
303 | ctrl |= CTRL_INV_VS; |
304 | if (bus_flags & DRM_BUS_FLAG_DE_LOW) |
305 | ctrl |= CTRL_INV_DE; |
306 | if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) |
307 | ctrl |= CTRL_INV_PXCK; |
308 | |
309 | writel(val: ctrl, addr: lcdif->base + LCDC_V8_CTRL); |
310 | |
311 | writel(DISP_SIZE_DELTA_Y(m->vdisplay) | |
312 | DISP_SIZE_DELTA_X(m->hdisplay), |
313 | addr: lcdif->base + LCDC_V8_DISP_SIZE); |
314 | |
315 | writel(HSYN_PARA_BP_H(m->htotal - m->hsync_end) | |
316 | HSYN_PARA_FP_H(m->hsync_start - m->hdisplay), |
317 | addr: lcdif->base + LCDC_V8_HSYN_PARA); |
318 | |
319 | writel(VSYN_PARA_BP_V(m->vtotal - m->vsync_end) | |
320 | VSYN_PARA_FP_V(m->vsync_start - m->vdisplay), |
321 | addr: lcdif->base + LCDC_V8_VSYN_PARA); |
322 | |
323 | writel(VSYN_HSYN_WIDTH_PW_V(m->vsync_end - m->vsync_start) | |
324 | VSYN_HSYN_WIDTH_PW_H(m->hsync_end - m->hsync_start), |
325 | addr: lcdif->base + LCDC_V8_VSYN_HSYN_WIDTH); |
326 | |
327 | writel(CTRLDESCL0_1_HEIGHT(m->vdisplay) | |
328 | CTRLDESCL0_1_WIDTH(m->hdisplay), |
329 | addr: lcdif->base + LCDC_V8_CTRLDESCL0_1); |
330 | |
331 | /* |
332 | * Undocumented P_SIZE and T_SIZE register but those written in the |
333 | * downstream kernel those registers control the AXI burst size. As of |
334 | * now there are two known values: |
335 | * 1 - 128Byte |
336 | * 2 - 256Byte |
337 | * Downstream set it to 256B burst size to improve the memory |
338 | * efficiency so set it here too. |
339 | */ |
340 | ctrl = CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) | |
341 | CTRLDESCL0_3_PITCH(lcdif->crtc.primary->state->fb->pitches[0]); |
342 | writel(val: ctrl, addr: lcdif->base + LCDC_V8_CTRLDESCL0_3); |
343 | } |
344 | |
345 | static void lcdif_enable_controller(struct lcdif_drm_private *lcdif) |
346 | { |
347 | u32 reg; |
348 | |
349 | /* Set FIFO Panic watermarks, low 1/3, high 2/3 . */ |
350 | writel(FIELD_PREP(PANIC0_THRES_LOW_MASK, 1 * PANIC0_THRES_MAX / 3) | |
351 | FIELD_PREP(PANIC0_THRES_HIGH_MASK, 2 * PANIC0_THRES_MAX / 3), |
352 | addr: lcdif->base + LCDC_V8_PANIC0_THRES); |
353 | |
354 | /* |
355 | * Enable FIFO Panic, this does not generate interrupt, but |
356 | * boosts NoC priority based on FIFO Panic watermarks. |
357 | */ |
358 | writel(INT_ENABLE_D1_PLANE_PANIC_EN, |
359 | addr: lcdif->base + LCDC_V8_INT_ENABLE_D1); |
360 | |
361 | reg = readl(addr: lcdif->base + LCDC_V8_DISP_PARA); |
362 | reg |= DISP_PARA_DISP_ON; |
363 | writel(val: reg, addr: lcdif->base + LCDC_V8_DISP_PARA); |
364 | |
365 | reg = readl(addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
366 | reg |= CTRLDESCL0_5_EN; |
367 | writel(val: reg, addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
368 | } |
369 | |
370 | static void lcdif_disable_controller(struct lcdif_drm_private *lcdif) |
371 | { |
372 | u32 reg; |
373 | int ret; |
374 | |
375 | reg = readl(addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
376 | reg &= ~CTRLDESCL0_5_EN; |
377 | writel(val: reg, addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
378 | |
379 | ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5, |
380 | reg, !(reg & CTRLDESCL0_5_EN), |
381 | 0, 36000); /* Wait ~2 frame times max */ |
382 | if (ret) |
383 | drm_err(lcdif->drm, "Failed to disable controller!\n" ); |
384 | |
385 | reg = readl(addr: lcdif->base + LCDC_V8_DISP_PARA); |
386 | reg &= ~DISP_PARA_DISP_ON; |
387 | writel(val: reg, addr: lcdif->base + LCDC_V8_DISP_PARA); |
388 | |
389 | /* Disable FIFO Panic NoC priority booster. */ |
390 | writel(val: 0, addr: lcdif->base + LCDC_V8_INT_ENABLE_D1); |
391 | } |
392 | |
393 | static void lcdif_reset_block(struct lcdif_drm_private *lcdif) |
394 | { |
395 | writel(CTRL_SW_RESET, addr: lcdif->base + LCDC_V8_CTRL + REG_SET); |
396 | readl(addr: lcdif->base + LCDC_V8_CTRL); |
397 | writel(CTRL_SW_RESET, addr: lcdif->base + LCDC_V8_CTRL + REG_CLR); |
398 | readl(addr: lcdif->base + LCDC_V8_CTRL); |
399 | } |
400 | |
401 | static void lcdif_crtc_mode_set_nofb(struct drm_crtc_state *crtc_state, |
402 | struct drm_plane_state *plane_state) |
403 | { |
404 | struct lcdif_crtc_state *lcdif_crtc_state = to_lcdif_crtc_state(s: crtc_state); |
405 | struct drm_device *drm = crtc_state->crtc->dev; |
406 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm); |
407 | struct drm_display_mode *m = &crtc_state->adjusted_mode; |
408 | |
409 | DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n" , |
410 | m->crtc_clock, |
411 | (int)(clk_get_rate(lcdif->clk) / 1000)); |
412 | DRM_DEV_DEBUG_DRIVER(drm->dev, "Bridge bus_flags: 0x%08X\n" , |
413 | lcdif_crtc_state->bus_flags); |
414 | DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n" , m->flags); |
415 | |
416 | /* Mandatory eLCDIF reset as per the Reference Manual */ |
417 | lcdif_reset_block(lcdif); |
418 | |
419 | lcdif_set_formats(lcdif, plane_state, bus_format: lcdif_crtc_state->bus_format); |
420 | |
421 | lcdif_set_mode(lcdif, bus_flags: lcdif_crtc_state->bus_flags); |
422 | } |
423 | |
424 | static int lcdif_crtc_atomic_check(struct drm_crtc *crtc, |
425 | struct drm_atomic_state *state) |
426 | { |
427 | struct drm_device *drm = crtc->dev; |
428 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
429 | crtc); |
430 | struct lcdif_crtc_state *lcdif_crtc_state = to_lcdif_crtc_state(s: crtc_state); |
431 | bool has_primary = crtc_state->plane_mask & |
432 | drm_plane_mask(plane: crtc->primary); |
433 | struct drm_connector_state *connector_state; |
434 | struct drm_connector *connector; |
435 | struct drm_encoder *encoder; |
436 | struct drm_bridge_state *bridge_state; |
437 | struct drm_bridge *bridge; |
438 | u32 bus_format, bus_flags; |
439 | bool format_set = false, flags_set = false; |
440 | int ret, i; |
441 | |
442 | /* The primary plane has to be enabled when the CRTC is active. */ |
443 | if (crtc_state->active && !has_primary) |
444 | return -EINVAL; |
445 | |
446 | ret = drm_atomic_add_affected_planes(state, crtc); |
447 | if (ret) |
448 | return ret; |
449 | |
450 | /* Try to find consistent bus format and flags across first bridges. */ |
451 | for_each_new_connector_in_state(state, connector, connector_state, i) { |
452 | if (!connector_state->crtc) |
453 | continue; |
454 | |
455 | encoder = connector_state->best_encoder; |
456 | |
457 | bridge = drm_bridge_chain_get_first_bridge(encoder); |
458 | if (!bridge) |
459 | continue; |
460 | |
461 | bridge_state = drm_atomic_get_new_bridge_state(state, bridge); |
462 | if (!bridge_state) |
463 | bus_format = MEDIA_BUS_FMT_FIXED; |
464 | else |
465 | bus_format = bridge_state->input_bus_cfg.format; |
466 | |
467 | if (bus_format == MEDIA_BUS_FMT_FIXED) { |
468 | dev_warn(drm->dev, |
469 | "[ENCODER:%d:%s]'s bridge does not provide bus format, assuming MEDIA_BUS_FMT_RGB888_1X24.\n" |
470 | "Please fix bridge driver by handling atomic_get_input_bus_fmts.\n" , |
471 | encoder->base.id, encoder->name); |
472 | bus_format = MEDIA_BUS_FMT_RGB888_1X24; |
473 | } else if (!bus_format) { |
474 | /* If all else fails, default to RGB888_1X24 */ |
475 | bus_format = MEDIA_BUS_FMT_RGB888_1X24; |
476 | } |
477 | |
478 | if (!format_set) { |
479 | lcdif_crtc_state->bus_format = bus_format; |
480 | format_set = true; |
481 | } else if (lcdif_crtc_state->bus_format != bus_format) { |
482 | DRM_DEV_DEBUG_DRIVER(drm->dev, "inconsistent bus format\n" ); |
483 | return -EINVAL; |
484 | } |
485 | |
486 | if (bridge->timings) |
487 | bus_flags = bridge->timings->input_bus_flags; |
488 | else if (bridge_state) |
489 | bus_flags = bridge_state->input_bus_cfg.flags; |
490 | else |
491 | bus_flags = 0; |
492 | |
493 | if (!flags_set) { |
494 | lcdif_crtc_state->bus_flags = bus_flags; |
495 | flags_set = true; |
496 | } else if (lcdif_crtc_state->bus_flags != bus_flags) { |
497 | DRM_DEV_DEBUG_DRIVER(drm->dev, "inconsistent bus flags\n" ); |
498 | return -EINVAL; |
499 | } |
500 | } |
501 | |
502 | return 0; |
503 | } |
504 | |
505 | static void lcdif_crtc_atomic_flush(struct drm_crtc *crtc, |
506 | struct drm_atomic_state *state) |
507 | { |
508 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm: crtc->dev); |
509 | struct drm_pending_vblank_event *event; |
510 | u32 reg; |
511 | |
512 | reg = readl(addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
513 | reg |= CTRLDESCL0_5_SHADOW_LOAD_EN; |
514 | writel(val: reg, addr: lcdif->base + LCDC_V8_CTRLDESCL0_5); |
515 | |
516 | event = crtc->state->event; |
517 | crtc->state->event = NULL; |
518 | |
519 | if (!event) |
520 | return; |
521 | |
522 | spin_lock_irq(lock: &crtc->dev->event_lock); |
523 | if (drm_crtc_vblank_get(crtc) == 0) |
524 | drm_crtc_arm_vblank_event(crtc, e: event); |
525 | else |
526 | drm_crtc_send_vblank_event(crtc, e: event); |
527 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
528 | } |
529 | |
530 | static void lcdif_crtc_atomic_enable(struct drm_crtc *crtc, |
531 | struct drm_atomic_state *state) |
532 | { |
533 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm: crtc->dev); |
534 | struct drm_crtc_state *new_cstate = drm_atomic_get_new_crtc_state(state, crtc); |
535 | struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state, |
536 | plane: crtc->primary); |
537 | struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode; |
538 | struct drm_device *drm = lcdif->drm; |
539 | dma_addr_t paddr; |
540 | |
541 | clk_set_rate(clk: lcdif->clk, rate: m->crtc_clock * 1000); |
542 | |
543 | pm_runtime_get_sync(dev: drm->dev); |
544 | |
545 | lcdif_crtc_mode_set_nofb(crtc_state: new_cstate, plane_state: new_pstate); |
546 | |
547 | /* Write cur_buf as well to avoid an initial corrupt frame */ |
548 | paddr = drm_fb_dma_get_gem_addr(fb: new_pstate->fb, state: new_pstate, plane: 0); |
549 | if (paddr) { |
550 | writel(lower_32_bits(paddr), |
551 | addr: lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4); |
552 | writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)), |
553 | addr: lcdif->base + LCDC_V8_CTRLDESCL_HIGH0_4); |
554 | } |
555 | lcdif_enable_controller(lcdif); |
556 | |
557 | drm_crtc_vblank_on(crtc); |
558 | } |
559 | |
560 | static void lcdif_crtc_atomic_disable(struct drm_crtc *crtc, |
561 | struct drm_atomic_state *state) |
562 | { |
563 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm: crtc->dev); |
564 | struct drm_device *drm = lcdif->drm; |
565 | struct drm_pending_vblank_event *event; |
566 | |
567 | drm_crtc_vblank_off(crtc); |
568 | |
569 | lcdif_disable_controller(lcdif); |
570 | |
571 | spin_lock_irq(lock: &drm->event_lock); |
572 | event = crtc->state->event; |
573 | if (event) { |
574 | crtc->state->event = NULL; |
575 | drm_crtc_send_vblank_event(crtc, e: event); |
576 | } |
577 | spin_unlock_irq(lock: &drm->event_lock); |
578 | |
579 | pm_runtime_put_sync(dev: drm->dev); |
580 | } |
581 | |
582 | static void lcdif_crtc_atomic_destroy_state(struct drm_crtc *crtc, |
583 | struct drm_crtc_state *state) |
584 | { |
585 | __drm_atomic_helper_crtc_destroy_state(state); |
586 | kfree(objp: to_lcdif_crtc_state(s: state)); |
587 | } |
588 | |
589 | static void lcdif_crtc_reset(struct drm_crtc *crtc) |
590 | { |
591 | struct lcdif_crtc_state *state; |
592 | |
593 | if (crtc->state) |
594 | lcdif_crtc_atomic_destroy_state(crtc, state: crtc->state); |
595 | |
596 | crtc->state = NULL; |
597 | |
598 | state = kzalloc(size: sizeof(*state), GFP_KERNEL); |
599 | if (state) |
600 | __drm_atomic_helper_crtc_reset(crtc, state: &state->base); |
601 | } |
602 | |
603 | static struct drm_crtc_state * |
604 | lcdif_crtc_atomic_duplicate_state(struct drm_crtc *crtc) |
605 | { |
606 | struct lcdif_crtc_state *old = to_lcdif_crtc_state(s: crtc->state); |
607 | struct lcdif_crtc_state *new; |
608 | |
609 | if (WARN_ON(!crtc->state)) |
610 | return NULL; |
611 | |
612 | new = kzalloc(size: sizeof(*new), GFP_KERNEL); |
613 | if (!new) |
614 | return NULL; |
615 | |
616 | __drm_atomic_helper_crtc_duplicate_state(crtc, state: &new->base); |
617 | |
618 | new->bus_format = old->bus_format; |
619 | new->bus_flags = old->bus_flags; |
620 | |
621 | return &new->base; |
622 | } |
623 | |
624 | static int lcdif_crtc_enable_vblank(struct drm_crtc *crtc) |
625 | { |
626 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm: crtc->dev); |
627 | |
628 | /* Clear and enable VBLANK IRQ */ |
629 | writel(INT_STATUS_D0_VS_BLANK, addr: lcdif->base + LCDC_V8_INT_STATUS_D0); |
630 | writel(INT_ENABLE_D0_VS_BLANK_EN, addr: lcdif->base + LCDC_V8_INT_ENABLE_D0); |
631 | |
632 | return 0; |
633 | } |
634 | |
635 | static void lcdif_crtc_disable_vblank(struct drm_crtc *crtc) |
636 | { |
637 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm: crtc->dev); |
638 | |
639 | /* Disable and clear VBLANK IRQ */ |
640 | writel(val: 0, addr: lcdif->base + LCDC_V8_INT_ENABLE_D0); |
641 | writel(INT_STATUS_D0_VS_BLANK, addr: lcdif->base + LCDC_V8_INT_STATUS_D0); |
642 | } |
643 | |
644 | static const struct drm_crtc_helper_funcs lcdif_crtc_helper_funcs = { |
645 | .atomic_check = lcdif_crtc_atomic_check, |
646 | .atomic_flush = lcdif_crtc_atomic_flush, |
647 | .atomic_enable = lcdif_crtc_atomic_enable, |
648 | .atomic_disable = lcdif_crtc_atomic_disable, |
649 | }; |
650 | |
651 | static const struct drm_crtc_funcs lcdif_crtc_funcs = { |
652 | .reset = lcdif_crtc_reset, |
653 | .destroy = drm_crtc_cleanup, |
654 | .set_config = drm_atomic_helper_set_config, |
655 | .page_flip = drm_atomic_helper_page_flip, |
656 | .atomic_duplicate_state = lcdif_crtc_atomic_duplicate_state, |
657 | .atomic_destroy_state = lcdif_crtc_atomic_destroy_state, |
658 | .enable_vblank = lcdif_crtc_enable_vblank, |
659 | .disable_vblank = lcdif_crtc_disable_vblank, |
660 | }; |
661 | |
662 | /* ----------------------------------------------------------------------------- |
663 | * Planes |
664 | */ |
665 | |
666 | static int lcdif_plane_atomic_check(struct drm_plane *plane, |
667 | struct drm_atomic_state *state) |
668 | { |
669 | struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, |
670 | plane); |
671 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm: plane->dev); |
672 | struct drm_crtc_state *crtc_state; |
673 | |
674 | crtc_state = drm_atomic_get_new_crtc_state(state, |
675 | crtc: &lcdif->crtc); |
676 | |
677 | return drm_atomic_helper_check_plane_state(plane_state, crtc_state, |
678 | DRM_PLANE_NO_SCALING, |
679 | DRM_PLANE_NO_SCALING, |
680 | can_position: false, can_update_disabled: true); |
681 | } |
682 | |
683 | static void lcdif_plane_primary_atomic_update(struct drm_plane *plane, |
684 | struct drm_atomic_state *state) |
685 | { |
686 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(drm: plane->dev); |
687 | struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state, |
688 | plane); |
689 | dma_addr_t paddr; |
690 | |
691 | paddr = drm_fb_dma_get_gem_addr(fb: new_pstate->fb, state: new_pstate, plane: 0); |
692 | if (paddr) { |
693 | writel(lower_32_bits(paddr), |
694 | addr: lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4); |
695 | writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)), |
696 | addr: lcdif->base + LCDC_V8_CTRLDESCL_HIGH0_4); |
697 | } |
698 | } |
699 | |
700 | static bool lcdif_format_mod_supported(struct drm_plane *plane, |
701 | uint32_t format, |
702 | uint64_t modifier) |
703 | { |
704 | return modifier == DRM_FORMAT_MOD_LINEAR; |
705 | } |
706 | |
707 | static const struct drm_plane_helper_funcs lcdif_plane_primary_helper_funcs = { |
708 | .atomic_check = lcdif_plane_atomic_check, |
709 | .atomic_update = lcdif_plane_primary_atomic_update, |
710 | }; |
711 | |
712 | static const struct drm_plane_funcs lcdif_plane_funcs = { |
713 | .format_mod_supported = lcdif_format_mod_supported, |
714 | .update_plane = drm_atomic_helper_update_plane, |
715 | .disable_plane = drm_atomic_helper_disable_plane, |
716 | .destroy = drm_plane_cleanup, |
717 | .reset = drm_atomic_helper_plane_reset, |
718 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
719 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
720 | }; |
721 | |
722 | static const u32 lcdif_primary_plane_formats[] = { |
723 | /* RGB */ |
724 | DRM_FORMAT_RGB565, |
725 | DRM_FORMAT_RGB888, |
726 | DRM_FORMAT_XBGR8888, |
727 | DRM_FORMAT_XRGB1555, |
728 | DRM_FORMAT_XRGB4444, |
729 | DRM_FORMAT_XRGB8888, |
730 | |
731 | /* Packed YCbCr */ |
732 | DRM_FORMAT_YUYV, |
733 | DRM_FORMAT_YVYU, |
734 | DRM_FORMAT_UYVY, |
735 | DRM_FORMAT_VYUY, |
736 | }; |
737 | |
738 | static const u64 lcdif_modifiers[] = { |
739 | DRM_FORMAT_MOD_LINEAR, |
740 | DRM_FORMAT_MOD_INVALID |
741 | }; |
742 | |
743 | /* ----------------------------------------------------------------------------- |
744 | * Initialization |
745 | */ |
746 | |
747 | int lcdif_kms_init(struct lcdif_drm_private *lcdif) |
748 | { |
749 | const u32 supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) | |
750 | BIT(DRM_COLOR_YCBCR_BT709) | |
751 | BIT(DRM_COLOR_YCBCR_BT2020); |
752 | const u32 supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | |
753 | BIT(DRM_COLOR_YCBCR_FULL_RANGE); |
754 | struct drm_crtc *crtc = &lcdif->crtc; |
755 | int ret; |
756 | |
757 | drm_plane_helper_add(plane: &lcdif->planes.primary, |
758 | funcs: &lcdif_plane_primary_helper_funcs); |
759 | ret = drm_universal_plane_init(dev: lcdif->drm, plane: &lcdif->planes.primary, possible_crtcs: 1, |
760 | funcs: &lcdif_plane_funcs, |
761 | formats: lcdif_primary_plane_formats, |
762 | ARRAY_SIZE(lcdif_primary_plane_formats), |
763 | format_modifiers: lcdif_modifiers, type: DRM_PLANE_TYPE_PRIMARY, |
764 | NULL); |
765 | if (ret) |
766 | return ret; |
767 | |
768 | ret = drm_plane_create_color_properties(plane: &lcdif->planes.primary, |
769 | supported_encodings, |
770 | supported_ranges, |
771 | default_encoding: DRM_COLOR_YCBCR_BT601, |
772 | default_range: DRM_COLOR_YCBCR_LIMITED_RANGE); |
773 | if (ret) |
774 | return ret; |
775 | |
776 | drm_crtc_helper_add(crtc, funcs: &lcdif_crtc_helper_funcs); |
777 | return drm_crtc_init_with_planes(dev: lcdif->drm, crtc, |
778 | primary: &lcdif->planes.primary, NULL, |
779 | funcs: &lcdif_crtc_funcs, NULL); |
780 | } |
781 | |