Commit | Line | Data |
---|---|---|
9db35bb3 MV |
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 | ||
e3cac8f7 | 8 | #include <linux/bitfield.h> |
9db35bb3 MV |
9 | #include <linux/clk.h> |
10 | #include <linux/io.h> | |
11 | #include <linux/iopoll.h> | |
72bd9ea3 | 12 | #include <linux/media-bus-format.h> |
9db35bb3 MV |
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> | |
6cba31e3 | 19 | #include <drm/drm_color_mgmt.h> |
c62a7b9c | 20 | #include <drm/drm_connector.h> |
9db35bb3 MV |
21 | #include <drm/drm_crtc.h> |
22 | #include <drm/drm_encoder.h> | |
6bcfe8ea | 23 | #include <drm/drm_fb_dma_helper.h> |
9db35bb3 | 24 | #include <drm/drm_fourcc.h> |
fcfd3e5f | 25 | #include <drm/drm_framebuffer.h> |
9db35bb3 | 26 | #include <drm/drm_gem_atomic_helper.h> |
4a83c26a | 27 | #include <drm/drm_gem_dma_helper.h> |
9db35bb3 | 28 | #include <drm/drm_plane.h> |
9db35bb3 MV |
29 | #include <drm/drm_vblank.h> |
30 | ||
31 | #include "lcdif_drv.h" | |
32 | #include "lcdif_regs.h" | |
33 | ||
5befcdca LY |
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 | ||
9db35bb3 MV |
46 | /* ----------------------------------------------------------------------------- |
47 | * CRTC | |
48 | */ | |
6cba31e3 KB |
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 | ||
9db35bb3 | 161 | static void lcdif_set_formats(struct lcdif_drm_private *lcdif, |
6cba31e3 | 162 | struct drm_plane_state *plane_state, |
9db35bb3 MV |
163 | const u32 bus_format) |
164 | { | |
165 | struct drm_device *drm = lcdif->drm; | |
6cba31e3 KB |
166 | const u32 format = plane_state->fb->format->format; |
167 | bool in_yuv = false; | |
168 | bool out_yuv = false; | |
9db35bb3 MV |
169 | |
170 | switch (bus_format) { | |
171 | case MEDIA_BUS_FMT_RGB565_1X16: | |
172 | writel(DISP_PARA_LINE_PATTERN_RGB565, | |
173 | lcdif->base + LCDC_V8_DISP_PARA); | |
174 | break; | |
175 | case MEDIA_BUS_FMT_RGB888_1X24: | |
176 | writel(DISP_PARA_LINE_PATTERN_RGB888, | |
177 | lcdif->base + LCDC_V8_DISP_PARA); | |
178 | break; | |
179 | case MEDIA_BUS_FMT_UYVY8_1X16: | |
180 | writel(DISP_PARA_LINE_PATTERN_UYVY_H, | |
181 | lcdif->base + LCDC_V8_DISP_PARA); | |
6cba31e3 | 182 | out_yuv = true; |
9db35bb3 MV |
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) { | |
6cba31e3 | 190 | /* RGB Formats */ |
9db35bb3 MV |
191 | case DRM_FORMAT_RGB565: |
192 | writel(CTRLDESCL0_5_BPP_16_RGB565, | |
193 | lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
194 | break; | |
195 | case DRM_FORMAT_RGB888: | |
196 | writel(CTRLDESCL0_5_BPP_24_RGB888, | |
197 | lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
198 | break; | |
199 | case DRM_FORMAT_XRGB1555: | |
200 | writel(CTRLDESCL0_5_BPP_16_ARGB1555, | |
201 | lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
202 | break; | |
203 | case DRM_FORMAT_XRGB4444: | |
204 | writel(CTRLDESCL0_5_BPP_16_ARGB4444, | |
205 | lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
206 | break; | |
207 | case DRM_FORMAT_XBGR8888: | |
208 | writel(CTRLDESCL0_5_BPP_32_ABGR8888, | |
209 | lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
210 | break; | |
211 | case DRM_FORMAT_XRGB8888: | |
212 | writel(CTRLDESCL0_5_BPP_32_ARGB8888, | |
213 | lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
214 | break; | |
6cba31e3 KB |
215 | |
216 | /* YUV Formats */ | |
217 | case DRM_FORMAT_YUYV: | |
218 | writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_VY2UY1, | |
219 | 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 | 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 | 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 | lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
235 | in_yuv = true; | |
236 | break; | |
237 | ||
9db35bb3 MV |
238 | default: |
239 | dev_err(drm->dev, "Unknown pixel format 0x%x\n", format); | |
240 | break; | |
241 | } | |
6cba31e3 KB |
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 | 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 | lcdif->base + LCDC_V8_CSC0_COEF0); | |
264 | writel(CSC0_COEF1_B1(0x7db) | CSC0_COEF1_A3(0x019), | |
265 | lcdif->base + LCDC_V8_CSC0_COEF1); | |
266 | writel(CSC0_COEF2_B3(0x070) | CSC0_COEF2_B2(0x7b6), | |
267 | lcdif->base + LCDC_V8_CSC0_COEF2); | |
268 | writel(CSC0_COEF3_C2(0x7a2) | CSC0_COEF3_C1(0x070), | |
269 | lcdif->base + LCDC_V8_CSC0_COEF3); | |
270 | writel(CSC0_COEF4_D1(0x010) | CSC0_COEF4_C3(0x7ee), | |
271 | lcdif->base + LCDC_V8_CSC0_COEF4); | |
272 | writel(CSC0_COEF5_D3(0x080) | CSC0_COEF5_D2(0x080), | |
273 | 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 | lcdif->base + LCDC_V8_CSC0_CTRL); | |
282 | ||
283 | writel(coeffs[0], lcdif->base + LCDC_V8_CSC0_COEF0); | |
284 | writel(coeffs[1], lcdif->base + LCDC_V8_CSC0_COEF1); | |
285 | writel(coeffs[2], lcdif->base + LCDC_V8_CSC0_COEF2); | |
286 | writel(coeffs[3], lcdif->base + LCDC_V8_CSC0_COEF3); | |
287 | writel(coeffs[4], lcdif->base + LCDC_V8_CSC0_COEF4); | |
288 | writel(coeffs[5], lcdif->base + LCDC_V8_CSC0_COEF5); | |
289 | } else { | |
290 | /* RGB -> RGB, YCbCr -> YCbCr: bypass colorspace converter. */ | |
291 | writel(CSC0_CTRL_BYPASS, lcdif->base + LCDC_V8_CSC0_CTRL); | |
292 | } | |
9db35bb3 MV |
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(ctrl, lcdif->base + LCDC_V8_CTRL); | |
310 | ||
5e6723ef MV |
311 | writel(DISP_SIZE_DELTA_Y(m->vdisplay) | |
312 | DISP_SIZE_DELTA_X(m->hdisplay), | |
9db35bb3 MV |
313 | 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 | 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 | 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 | lcdif->base + LCDC_V8_VSYN_HSYN_WIDTH); | |
326 | ||
5e6723ef MV |
327 | writel(CTRLDESCL0_1_HEIGHT(m->vdisplay) | |
328 | CTRLDESCL0_1_WIDTH(m->hdisplay), | |
9db35bb3 MV |
329 | lcdif->base + LCDC_V8_CTRLDESCL0_1); |
330 | ||
2215cb3b MF |
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(ctrl, lcdif->base + LCDC_V8_CTRLDESCL0_3); | |
9db35bb3 MV |
343 | } |
344 | ||
345 | static void lcdif_enable_controller(struct lcdif_drm_private *lcdif) | |
346 | { | |
347 | u32 reg; | |
348 | ||
e3cac8f7 MV |
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 | 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 | lcdif->base + LCDC_V8_INT_ENABLE_D1); | |
360 | ||
9db35bb3 MV |
361 | reg = readl(lcdif->base + LCDC_V8_DISP_PARA); |
362 | reg |= DISP_PARA_DISP_ON; | |
363 | writel(reg, lcdif->base + LCDC_V8_DISP_PARA); | |
364 | ||
365 | reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
366 | reg |= CTRLDESCL0_5_EN; | |
367 | writel(reg, 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(lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
376 | reg &= ~CTRLDESCL0_5_EN; | |
377 | writel(reg, 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(lcdif->base + LCDC_V8_DISP_PARA); | |
386 | reg &= ~DISP_PARA_DISP_ON; | |
387 | writel(reg, lcdif->base + LCDC_V8_DISP_PARA); | |
e3cac8f7 MV |
388 | |
389 | /* Disable FIFO Panic NoC priority booster. */ | |
390 | writel(0, lcdif->base + LCDC_V8_INT_ENABLE_D1); | |
9db35bb3 MV |
391 | } |
392 | ||
393 | static void lcdif_reset_block(struct lcdif_drm_private *lcdif) | |
394 | { | |
395 | writel(CTRL_SW_RESET, lcdif->base + LCDC_V8_CTRL + REG_SET); | |
396 | readl(lcdif->base + LCDC_V8_CTRL); | |
397 | writel(CTRL_SW_RESET, lcdif->base + LCDC_V8_CTRL + REG_CLR); | |
398 | readl(lcdif->base + LCDC_V8_CTRL); | |
399 | } | |
400 | ||
5befcdca LY |
401 | static void lcdif_crtc_mode_set_nofb(struct drm_crtc_state *crtc_state, |
402 | struct drm_plane_state *plane_state) | |
9db35bb3 | 403 | { |
5befcdca LY |
404 | struct lcdif_crtc_state *lcdif_crtc_state = to_lcdif_crtc_state(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; | |
9db35bb3 MV |
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)); | |
71c627c0 | 412 | DRM_DEV_DEBUG_DRIVER(drm->dev, "Bridge bus_flags: 0x%08X\n", |
5befcdca | 413 | lcdif_crtc_state->bus_flags); |
9db35bb3 MV |
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 | ||
5befcdca | 419 | lcdif_set_formats(lcdif, plane_state, lcdif_crtc_state->bus_format); |
9db35bb3 | 420 | |
5befcdca | 421 | lcdif_set_mode(lcdif, lcdif_crtc_state->bus_flags); |
9db35bb3 MV |
422 | } |
423 | ||
424 | static int lcdif_crtc_atomic_check(struct drm_crtc *crtc, | |
425 | struct drm_atomic_state *state) | |
426 | { | |
5befcdca | 427 | struct drm_device *drm = crtc->dev; |
9db35bb3 MV |
428 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, |
429 | crtc); | |
5befcdca | 430 | struct lcdif_crtc_state *lcdif_crtc_state = to_lcdif_crtc_state(crtc_state); |
9db35bb3 MV |
431 | bool has_primary = crtc_state->plane_mask & |
432 | drm_plane_mask(crtc->primary); | |
c62a7b9c LY |
433 | struct drm_connector_state *connector_state; |
434 | struct drm_connector *connector; | |
435 | struct drm_encoder *encoder; | |
5befcdca | 436 | struct drm_bridge_state *bridge_state; |
c62a7b9c LY |
437 | struct drm_bridge *bridge; |
438 | u32 bus_format, bus_flags; | |
439 | bool format_set = false, flags_set = false; | |
440 | int ret, i; | |
9db35bb3 MV |
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 | ||
5befcdca LY |
446 | ret = drm_atomic_add_affected_planes(state, crtc); |
447 | if (ret) | |
448 | return ret; | |
449 | ||
c62a7b9c LY |
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 | } | |
5befcdca LY |
500 | } |
501 | ||
5befcdca | 502 | return 0; |
9db35bb3 MV |
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(crtc->dev); | |
509 | struct drm_pending_vblank_event *event; | |
510 | u32 reg; | |
511 | ||
512 | reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5); | |
513 | reg |= CTRLDESCL0_5_SHADOW_LOAD_EN; | |
514 | writel(reg, 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(&crtc->dev->event_lock); | |
523 | if (drm_crtc_vblank_get(crtc) == 0) | |
524 | drm_crtc_arm_vblank_event(crtc, event); | |
525 | else | |
526 | drm_crtc_send_vblank_event(crtc, event); | |
527 | spin_unlock_irq(&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(crtc->dev); | |
5befcdca | 534 | struct drm_crtc_state *new_cstate = drm_atomic_get_new_crtc_state(state, crtc); |
9db35bb3 MV |
535 | struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state, |
536 | crtc->primary); | |
537 | struct drm_display_mode *m = &lcdif->crtc.state->adjusted_mode; | |
9db35bb3 | 538 | struct drm_device *drm = lcdif->drm; |
9db35bb3 MV |
539 | dma_addr_t paddr; |
540 | ||
9db35bb3 MV |
541 | clk_set_rate(lcdif->clk, m->crtc_clock * 1000); |
542 | ||
543 | pm_runtime_get_sync(drm->dev); | |
544 | ||
5befcdca | 545 | lcdif_crtc_mode_set_nofb(new_cstate, new_pstate); |
9db35bb3 MV |
546 | |
547 | /* Write cur_buf as well to avoid an initial corrupt frame */ | |
6bcfe8ea | 548 | paddr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0); |
9db35bb3 MV |
549 | if (paddr) { |
550 | writel(lower_32_bits(paddr), | |
551 | lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4); | |
552 | writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)), | |
553 | 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(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(&drm->event_lock); | |
572 | event = crtc->state->event; | |
573 | if (event) { | |
574 | crtc->state->event = NULL; | |
575 | drm_crtc_send_vblank_event(crtc, event); | |
576 | } | |
577 | spin_unlock_irq(&drm->event_lock); | |
578 | ||
579 | pm_runtime_put_sync(drm->dev); | |
580 | } | |
581 | ||
5befcdca LY |
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(to_lcdif_crtc_state(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, crtc->state); | |
595 | ||
596 | crtc->state = NULL; | |
597 | ||
598 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
599 | if (state) | |
600 | __drm_atomic_helper_crtc_reset(crtc, &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(crtc->state); | |
607 | struct lcdif_crtc_state *new; | |
608 | ||
609 | if (WARN_ON(!crtc->state)) | |
610 | return NULL; | |
611 | ||
612 | new = kzalloc(sizeof(*new), GFP_KERNEL); | |
613 | if (!new) | |
614 | return NULL; | |
615 | ||
616 | __drm_atomic_helper_crtc_duplicate_state(crtc, &new->base); | |
617 | ||
618 | new->bus_format = old->bus_format; | |
619 | new->bus_flags = old->bus_flags; | |
620 | ||
621 | return &new->base; | |
622 | } | |
623 | ||
9db35bb3 MV |
624 | static int lcdif_crtc_enable_vblank(struct drm_crtc *crtc) |
625 | { | |
626 | struct lcdif_drm_private *lcdif = to_lcdif_drm_private(crtc->dev); | |
627 | ||
628 | /* Clear and enable VBLANK IRQ */ | |
629 | writel(INT_STATUS_D0_VS_BLANK, lcdif->base + LCDC_V8_INT_STATUS_D0); | |
630 | writel(INT_ENABLE_D0_VS_BLANK_EN, 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(crtc->dev); | |
638 | ||
639 | /* Disable and clear VBLANK IRQ */ | |
640 | writel(0, lcdif->base + LCDC_V8_INT_ENABLE_D0); | |
641 | writel(INT_STATUS_D0_VS_BLANK, 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 = { | |
5befcdca | 652 | .reset = lcdif_crtc_reset, |
9db35bb3 MV |
653 | .destroy = drm_crtc_cleanup, |
654 | .set_config = drm_atomic_helper_set_config, | |
655 | .page_flip = drm_atomic_helper_page_flip, | |
5befcdca LY |
656 | .atomic_duplicate_state = lcdif_crtc_atomic_duplicate_state, |
657 | .atomic_destroy_state = lcdif_crtc_atomic_destroy_state, | |
9db35bb3 MV |
658 | .enable_vblank = lcdif_crtc_enable_vblank, |
659 | .disable_vblank = lcdif_crtc_disable_vblank, | |
660 | }; | |
661 | ||
9db35bb3 MV |
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(plane->dev); | |
672 | struct drm_crtc_state *crtc_state; | |
673 | ||
674 | crtc_state = drm_atomic_get_new_crtc_state(state, | |
675 | &lcdif->crtc); | |
676 | ||
677 | return drm_atomic_helper_check_plane_state(plane_state, crtc_state, | |
cce32e4e TZ |
678 | DRM_PLANE_NO_SCALING, |
679 | DRM_PLANE_NO_SCALING, | |
9db35bb3 MV |
680 | false, 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(plane->dev); | |
687 | struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state, | |
688 | plane); | |
689 | dma_addr_t paddr; | |
690 | ||
6bcfe8ea | 691 | paddr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0); |
9db35bb3 MV |
692 | if (paddr) { |
693 | writel(lower_32_bits(paddr), | |
694 | lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4); | |
695 | writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)), | |
696 | 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[] = { | |
6cba31e3 | 723 | /* RGB */ |
9db35bb3 MV |
724 | DRM_FORMAT_RGB565, |
725 | DRM_FORMAT_RGB888, | |
726 | DRM_FORMAT_XBGR8888, | |
727 | DRM_FORMAT_XRGB1555, | |
728 | DRM_FORMAT_XRGB4444, | |
729 | DRM_FORMAT_XRGB8888, | |
6cba31e3 KB |
730 | |
731 | /* Packed YCbCr */ | |
732 | DRM_FORMAT_YUYV, | |
733 | DRM_FORMAT_YVYU, | |
734 | DRM_FORMAT_UYVY, | |
735 | DRM_FORMAT_VYUY, | |
9db35bb3 MV |
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 | { | |
6cba31e3 KB |
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); | |
9db35bb3 MV |
754 | struct drm_crtc *crtc = &lcdif->crtc; |
755 | int ret; | |
756 | ||
757 | drm_plane_helper_add(&lcdif->planes.primary, | |
758 | &lcdif_plane_primary_helper_funcs); | |
759 | ret = drm_universal_plane_init(lcdif->drm, &lcdif->planes.primary, 1, | |
760 | &lcdif_plane_funcs, | |
761 | lcdif_primary_plane_formats, | |
762 | ARRAY_SIZE(lcdif_primary_plane_formats), | |
763 | lcdif_modifiers, DRM_PLANE_TYPE_PRIMARY, | |
764 | NULL); | |
765 | if (ret) | |
766 | return ret; | |
767 | ||
6cba31e3 KB |
768 | ret = drm_plane_create_color_properties(&lcdif->planes.primary, |
769 | supported_encodings, | |
770 | supported_ranges, | |
771 | DRM_COLOR_YCBCR_BT601, | |
772 | DRM_COLOR_YCBCR_LIMITED_RANGE); | |
773 | if (ret) | |
774 | return ret; | |
775 | ||
9db35bb3 | 776 | drm_crtc_helper_add(crtc, &lcdif_crtc_helper_funcs); |
dbb32d85 LY |
777 | return drm_crtc_init_with_planes(lcdif->drm, crtc, |
778 | &lcdif->planes.primary, NULL, | |
779 | &lcdif_crtc_funcs, NULL); | |
9db35bb3 | 780 | } |