]>
Commit | Line | Data |
---|---|---|
c8b75bca EA |
1 | /* |
2 | * Copyright (C) 2015 Broadcom | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | /** | |
10 | * DOC: VC4 plane module | |
11 | * | |
12 | * Each DRM plane is a layer of pixels being scanned out by the HVS. | |
13 | * | |
14 | * At atomic modeset check time, we compute the HVS display element | |
15 | * state that would be necessary for displaying the plane (giving us a | |
16 | * chance to figure out if a plane configuration is invalid), then at | |
17 | * atomic flush time the CRTC will ask us to write our element state | |
18 | * into the region of the HVS that it has allocated for us. | |
19 | */ | |
20 | ||
b7e8e25b MY |
21 | #include <drm/drm_atomic.h> |
22 | #include <drm/drm_atomic_helper.h> | |
23 | #include <drm/drm_fb_cma_helper.h> | |
24 | #include <drm/drm_plane_helper.h> | |
72fdb40c | 25 | #include <drm/drm_atomic_uapi.h> |
b7e8e25b | 26 | |
b9f19259 | 27 | #include "uapi/drm/vc4_drm.h" |
c8b75bca EA |
28 | #include "vc4_drv.h" |
29 | #include "vc4_regs.h" | |
c8b75bca | 30 | |
c8b75bca EA |
31 | static const struct hvs_format { |
32 | u32 drm; /* DRM_FORMAT_* */ | |
33 | u32 hvs; /* HVS_FORMAT_* */ | |
34 | u32 pixel_order; | |
c8b75bca EA |
35 | } hvs_formats[] = { |
36 | { | |
37 | .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, | |
124e5dac | 38 | .pixel_order = HVS_PIXEL_ORDER_ABGR, |
c8b75bca EA |
39 | }, |
40 | { | |
41 | .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, | |
124e5dac | 42 | .pixel_order = HVS_PIXEL_ORDER_ABGR, |
c8b75bca | 43 | }, |
93977767 RH |
44 | { |
45 | .drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, | |
124e5dac | 46 | .pixel_order = HVS_PIXEL_ORDER_ARGB, |
93977767 RH |
47 | }, |
48 | { | |
49 | .drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, | |
124e5dac | 50 | .pixel_order = HVS_PIXEL_ORDER_ARGB, |
93977767 | 51 | }, |
fe4cd847 EA |
52 | { |
53 | .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565, | |
124e5dac | 54 | .pixel_order = HVS_PIXEL_ORDER_XRGB, |
fe4cd847 EA |
55 | }, |
56 | { | |
57 | .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565, | |
124e5dac | 58 | .pixel_order = HVS_PIXEL_ORDER_XBGR, |
fe4cd847 EA |
59 | }, |
60 | { | |
61 | .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, | |
124e5dac | 62 | .pixel_order = HVS_PIXEL_ORDER_ABGR, |
fe4cd847 EA |
63 | }, |
64 | { | |
65 | .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551, | |
124e5dac | 66 | .pixel_order = HVS_PIXEL_ORDER_ABGR, |
fe4cd847 | 67 | }, |
88f8156f DS |
68 | { |
69 | .drm = DRM_FORMAT_RGB888, .hvs = HVS_PIXEL_FORMAT_RGB888, | |
124e5dac | 70 | .pixel_order = HVS_PIXEL_ORDER_XRGB, |
88f8156f DS |
71 | }, |
72 | { | |
73 | .drm = DRM_FORMAT_BGR888, .hvs = HVS_PIXEL_FORMAT_RGB888, | |
124e5dac | 74 | .pixel_order = HVS_PIXEL_ORDER_XBGR, |
88f8156f | 75 | }, |
fc04023f EA |
76 | { |
77 | .drm = DRM_FORMAT_YUV422, | |
78 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, | |
090cb0c6 | 79 | .pixel_order = HVS_PIXEL_ORDER_XYCBCR, |
fc04023f EA |
80 | }, |
81 | { | |
82 | .drm = DRM_FORMAT_YVU422, | |
83 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE, | |
090cb0c6 | 84 | .pixel_order = HVS_PIXEL_ORDER_XYCRCB, |
fc04023f EA |
85 | }, |
86 | { | |
87 | .drm = DRM_FORMAT_YUV420, | |
88 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, | |
090cb0c6 | 89 | .pixel_order = HVS_PIXEL_ORDER_XYCBCR, |
fc04023f EA |
90 | }, |
91 | { | |
92 | .drm = DRM_FORMAT_YVU420, | |
93 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE, | |
090cb0c6 | 94 | .pixel_order = HVS_PIXEL_ORDER_XYCRCB, |
fc04023f EA |
95 | }, |
96 | { | |
97 | .drm = DRM_FORMAT_NV12, | |
98 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE, | |
090cb0c6 | 99 | .pixel_order = HVS_PIXEL_ORDER_XYCBCR, |
fc04023f | 100 | }, |
cb20dd17 DS |
101 | { |
102 | .drm = DRM_FORMAT_NV21, | |
103 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE, | |
104 | .pixel_order = HVS_PIXEL_ORDER_XYCRCB, | |
105 | }, | |
fc04023f EA |
106 | { |
107 | .drm = DRM_FORMAT_NV16, | |
108 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, | |
090cb0c6 | 109 | .pixel_order = HVS_PIXEL_ORDER_XYCBCR, |
fc04023f | 110 | }, |
cb20dd17 DS |
111 | { |
112 | .drm = DRM_FORMAT_NV61, | |
113 | .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE, | |
114 | .pixel_order = HVS_PIXEL_ORDER_XYCRCB, | |
115 | }, | |
c8b75bca EA |
116 | }; |
117 | ||
118 | static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) | |
119 | { | |
120 | unsigned i; | |
121 | ||
122 | for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { | |
123 | if (hvs_formats[i].drm == drm_format) | |
124 | return &hvs_formats[i]; | |
125 | } | |
126 | ||
127 | return NULL; | |
128 | } | |
129 | ||
21af94cf EA |
130 | static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) |
131 | { | |
132 | if (dst > src) | |
133 | return VC4_SCALING_PPF; | |
134 | else if (dst < src) | |
135 | return VC4_SCALING_TPZ; | |
136 | else | |
137 | return VC4_SCALING_NONE; | |
138 | } | |
139 | ||
c8b75bca EA |
140 | static bool plane_enabled(struct drm_plane_state *state) |
141 | { | |
142 | return state->fb && state->crtc; | |
143 | } | |
144 | ||
91276ae2 | 145 | static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane) |
c8b75bca EA |
146 | { |
147 | struct vc4_plane_state *vc4_state; | |
148 | ||
149 | if (WARN_ON(!plane->state)) | |
150 | return NULL; | |
151 | ||
152 | vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL); | |
153 | if (!vc4_state) | |
154 | return NULL; | |
155 | ||
21af94cf EA |
156 | memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm)); |
157 | ||
c8b75bca EA |
158 | __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base); |
159 | ||
160 | if (vc4_state->dlist) { | |
161 | vc4_state->dlist = kmemdup(vc4_state->dlist, | |
162 | vc4_state->dlist_count * 4, | |
163 | GFP_KERNEL); | |
164 | if (!vc4_state->dlist) { | |
165 | kfree(vc4_state); | |
166 | return NULL; | |
167 | } | |
168 | vc4_state->dlist_size = vc4_state->dlist_count; | |
169 | } | |
170 | ||
171 | return &vc4_state->base; | |
172 | } | |
173 | ||
91276ae2 | 174 | static void vc4_plane_destroy_state(struct drm_plane *plane, |
175 | struct drm_plane_state *state) | |
c8b75bca | 176 | { |
21af94cf | 177 | struct vc4_dev *vc4 = to_vc4_dev(plane->dev); |
c8b75bca EA |
178 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
179 | ||
21af94cf EA |
180 | if (vc4_state->lbm.allocated) { |
181 | unsigned long irqflags; | |
182 | ||
183 | spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); | |
184 | drm_mm_remove_node(&vc4_state->lbm); | |
185 | spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); | |
186 | } | |
187 | ||
c8b75bca | 188 | kfree(vc4_state->dlist); |
2f701695 | 189 | __drm_atomic_helper_plane_destroy_state(&vc4_state->base); |
c8b75bca EA |
190 | kfree(state); |
191 | } | |
192 | ||
193 | /* Called during init to allocate the plane's atomic state. */ | |
91276ae2 | 194 | static void vc4_plane_reset(struct drm_plane *plane) |
c8b75bca EA |
195 | { |
196 | struct vc4_plane_state *vc4_state; | |
197 | ||
198 | WARN_ON(plane->state); | |
199 | ||
200 | vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL); | |
201 | if (!vc4_state) | |
202 | return; | |
203 | ||
42da6338 | 204 | __drm_atomic_helper_plane_reset(plane, &vc4_state->base); |
c8b75bca EA |
205 | } |
206 | ||
207 | static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) | |
208 | { | |
209 | if (vc4_state->dlist_count == vc4_state->dlist_size) { | |
210 | u32 new_size = max(4u, vc4_state->dlist_count * 2); | |
6da2ec56 | 211 | u32 *new_dlist = kmalloc_array(new_size, 4, GFP_KERNEL); |
c8b75bca EA |
212 | |
213 | if (!new_dlist) | |
214 | return; | |
215 | memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4); | |
216 | ||
217 | kfree(vc4_state->dlist); | |
218 | vc4_state->dlist = new_dlist; | |
219 | vc4_state->dlist_size = new_size; | |
220 | } | |
221 | ||
222 | vc4_state->dlist[vc4_state->dlist_count++] = val; | |
223 | } | |
224 | ||
21af94cf EA |
225 | /* Returns the scl0/scl1 field based on whether the dimensions need to |
226 | * be up/down/non-scaled. | |
227 | * | |
228 | * This is a replication of a table from the spec. | |
229 | */ | |
fc04023f | 230 | static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane) |
21af94cf EA |
231 | { |
232 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | |
233 | ||
fc04023f | 234 | switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) { |
21af94cf EA |
235 | case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF: |
236 | return SCALER_CTL0_SCL_H_PPF_V_PPF; | |
237 | case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF: | |
238 | return SCALER_CTL0_SCL_H_TPZ_V_PPF; | |
239 | case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ: | |
240 | return SCALER_CTL0_SCL_H_PPF_V_TPZ; | |
241 | case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ: | |
242 | return SCALER_CTL0_SCL_H_TPZ_V_TPZ; | |
243 | case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE: | |
244 | return SCALER_CTL0_SCL_H_PPF_V_NONE; | |
245 | case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF: | |
246 | return SCALER_CTL0_SCL_H_NONE_V_PPF; | |
247 | case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ: | |
248 | return SCALER_CTL0_SCL_H_NONE_V_TPZ; | |
249 | case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE: | |
250 | return SCALER_CTL0_SCL_H_TPZ_V_NONE; | |
251 | default: | |
252 | case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE: | |
253 | /* The unity case is independently handled by | |
254 | * SCALER_CTL0_UNITY. | |
255 | */ | |
256 | return 0; | |
257 | } | |
258 | } | |
259 | ||
5c679994 | 260 | static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) |
c8b75bca | 261 | { |
21af94cf | 262 | struct drm_plane *plane = state->plane; |
c8b75bca EA |
263 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
264 | struct drm_framebuffer *fb = state->fb; | |
fc04023f | 265 | struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); |
21af94cf | 266 | u32 subpixel_src_mask = (1 << 16) - 1; |
438b74a5 | 267 | u32 format = fb->format->format; |
bcb0b461 | 268 | int num_planes = fb->format->num_planes; |
58a6a36f BB |
269 | int min_scale = 1, max_scale = INT_MAX; |
270 | struct drm_crtc_state *crtc_state; | |
271 | u32 h_subsample, v_subsample; | |
272 | int i, ret; | |
273 | ||
274 | crtc_state = drm_atomic_get_existing_crtc_state(state->state, | |
275 | state->crtc); | |
276 | if (!crtc_state) { | |
277 | DRM_DEBUG_KMS("Invalid crtc state\n"); | |
278 | return -EINVAL; | |
279 | } | |
280 | ||
281 | /* No configuring scaling on the cursor plane, since it gets | |
282 | * non-vblank-synced updates, and scaling requires LBM changes which | |
283 | * have to be vblank-synced. | |
284 | */ | |
285 | if (plane->type == DRM_PLANE_TYPE_CURSOR) { | |
286 | min_scale = DRM_PLANE_HELPER_NO_SCALING; | |
287 | max_scale = DRM_PLANE_HELPER_NO_SCALING; | |
288 | } else { | |
289 | min_scale = 1; | |
290 | max_scale = INT_MAX; | |
291 | } | |
292 | ||
293 | ret = drm_atomic_helper_check_plane_state(state, crtc_state, | |
294 | min_scale, max_scale, | |
295 | true, true); | |
296 | if (ret) | |
297 | return ret; | |
298 | ||
299 | h_subsample = drm_format_horz_chroma_subsampling(format); | |
300 | v_subsample = drm_format_vert_chroma_subsampling(format); | |
5c679994 | 301 | |
fc04023f EA |
302 | for (i = 0; i < num_planes; i++) |
303 | vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; | |
5c679994 | 304 | |
21af94cf | 305 | /* We don't support subpixel source positioning for scaling. */ |
58a6a36f BB |
306 | if ((state->src.x1 & subpixel_src_mask) || |
307 | (state->src.x2 & subpixel_src_mask) || | |
308 | (state->src.y1 & subpixel_src_mask) || | |
309 | (state->src.y2 & subpixel_src_mask)) { | |
bf893acc EA |
310 | return -EINVAL; |
311 | } | |
312 | ||
58a6a36f BB |
313 | vc4_state->src_x = state->src.x1 >> 16; |
314 | vc4_state->src_y = state->src.y1 >> 16; | |
315 | vc4_state->src_w[0] = (state->src.x2 - state->src.x1) >> 16; | |
316 | vc4_state->src_h[0] = (state->src.y2 - state->src.y1) >> 16; | |
f863e356 | 317 | |
58a6a36f BB |
318 | vc4_state->crtc_x = state->dst.x1; |
319 | vc4_state->crtc_y = state->dst.y1; | |
320 | vc4_state->crtc_w = state->dst.x2 - state->dst.x1; | |
321 | vc4_state->crtc_h = state->dst.y2 - state->dst.y1; | |
f863e356 | 322 | |
fc04023f EA |
323 | vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], |
324 | vc4_state->crtc_w); | |
325 | vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], | |
326 | vc4_state->crtc_h); | |
327 | ||
658d8cbd BB |
328 | vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE && |
329 | vc4_state->y_scaling[0] == VC4_SCALING_NONE); | |
330 | ||
fc04023f EA |
331 | if (num_planes > 1) { |
332 | vc4_state->is_yuv = true; | |
333 | ||
fc04023f EA |
334 | vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample; |
335 | vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample; | |
336 | ||
337 | vc4_state->x_scaling[1] = | |
338 | vc4_get_scaling_mode(vc4_state->src_w[1], | |
339 | vc4_state->crtc_w); | |
340 | vc4_state->y_scaling[1] = | |
341 | vc4_get_scaling_mode(vc4_state->src_h[1], | |
342 | vc4_state->crtc_h); | |
343 | ||
658d8cbd BB |
344 | /* YUV conversion requires that horizontal scaling be enabled, |
345 | * even on a plane that's otherwise 1:1. Looks like only PPF | |
346 | * works in that case, so let's pick that one. | |
fc04023f | 347 | */ |
658d8cbd BB |
348 | if (vc4_state->is_unity) |
349 | vc4_state->x_scaling[0] = VC4_SCALING_PPF; | |
a6a00918 BB |
350 | } else { |
351 | vc4_state->x_scaling[1] = VC4_SCALING_NONE; | |
352 | vc4_state->y_scaling[1] = VC4_SCALING_NONE; | |
fc04023f EA |
353 | } |
354 | ||
5c679994 EA |
355 | return 0; |
356 | } | |
357 | ||
21af94cf EA |
358 | static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst) |
359 | { | |
360 | u32 scale, recip; | |
361 | ||
362 | scale = (1 << 16) * src / dst; | |
363 | ||
364 | /* The specs note that while the reciprocal would be defined | |
365 | * as (1<<32)/scale, ~0 is close enough. | |
366 | */ | |
367 | recip = ~0 / scale; | |
368 | ||
369 | vc4_dlist_write(vc4_state, | |
370 | VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) | | |
371 | VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE)); | |
372 | vc4_dlist_write(vc4_state, | |
373 | VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP)); | |
374 | } | |
375 | ||
376 | static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst) | |
377 | { | |
378 | u32 scale = (1 << 16) * src / dst; | |
379 | ||
380 | vc4_dlist_write(vc4_state, | |
381 | SCALER_PPF_AGC | | |
382 | VC4_SET_FIELD(scale, SCALER_PPF_SCALE) | | |
383 | VC4_SET_FIELD(0, SCALER_PPF_IPHASE)); | |
384 | } | |
385 | ||
386 | static u32 vc4_lbm_size(struct drm_plane_state *state) | |
387 | { | |
388 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | |
389 | /* This is the worst case number. One of the two sizes will | |
390 | * be used depending on the scaling configuration. | |
391 | */ | |
fc04023f | 392 | u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w); |
21af94cf EA |
393 | u32 lbm; |
394 | ||
fc04023f EA |
395 | if (!vc4_state->is_yuv) { |
396 | if (vc4_state->is_unity) | |
397 | return 0; | |
398 | else if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) | |
399 | lbm = pix_per_line * 8; | |
400 | else { | |
401 | /* In special cases, this multiplier might be 12. */ | |
402 | lbm = pix_per_line * 16; | |
403 | } | |
404 | } else { | |
405 | /* There are cases for this going down to a multiplier | |
406 | * of 2, but according to the firmware source, the | |
407 | * table in the docs is somewhat wrong. | |
408 | */ | |
21af94cf EA |
409 | lbm = pix_per_line * 16; |
410 | } | |
411 | ||
412 | lbm = roundup(lbm, 32); | |
413 | ||
414 | return lbm; | |
415 | } | |
416 | ||
fc04023f EA |
417 | static void vc4_write_scaling_parameters(struct drm_plane_state *state, |
418 | int channel) | |
21af94cf EA |
419 | { |
420 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | |
421 | ||
422 | /* Ch0 H-PPF Word 0: Scaling Parameters */ | |
fc04023f | 423 | if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) { |
21af94cf | 424 | vc4_write_ppf(vc4_state, |
fc04023f | 425 | vc4_state->src_w[channel], vc4_state->crtc_w); |
21af94cf EA |
426 | } |
427 | ||
428 | /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */ | |
fc04023f | 429 | if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) { |
21af94cf | 430 | vc4_write_ppf(vc4_state, |
fc04023f | 431 | vc4_state->src_h[channel], vc4_state->crtc_h); |
21af94cf EA |
432 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); |
433 | } | |
434 | ||
435 | /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */ | |
fc04023f | 436 | if (vc4_state->x_scaling[channel] == VC4_SCALING_TPZ) { |
21af94cf | 437 | vc4_write_tpz(vc4_state, |
fc04023f | 438 | vc4_state->src_w[channel], vc4_state->crtc_w); |
21af94cf EA |
439 | } |
440 | ||
441 | /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */ | |
fc04023f | 442 | if (vc4_state->y_scaling[channel] == VC4_SCALING_TPZ) { |
21af94cf | 443 | vc4_write_tpz(vc4_state, |
fc04023f | 444 | vc4_state->src_h[channel], vc4_state->crtc_h); |
21af94cf EA |
445 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); |
446 | } | |
447 | } | |
5c679994 EA |
448 | |
449 | /* Writes out a full display list for an active plane to the plane's | |
450 | * private dlist state. | |
451 | */ | |
452 | static int vc4_plane_mode_set(struct drm_plane *plane, | |
453 | struct drm_plane_state *state) | |
454 | { | |
21af94cf | 455 | struct vc4_dev *vc4 = to_vc4_dev(plane->dev); |
5c679994 EA |
456 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
457 | struct drm_framebuffer *fb = state->fb; | |
5c679994 | 458 | u32 ctl0_offset = vc4_state->dlist_count; |
438b74a5 | 459 | const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); |
e065a8dd | 460 | u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier); |
fc04023f | 461 | int num_planes = drm_format_num_planes(format->drm); |
a65511b1 | 462 | u32 h_subsample, v_subsample; |
22445f03 | 463 | bool mix_plane_alpha; |
3d67b68a | 464 | bool covers_screen; |
98830d91 EA |
465 | u32 scl0, scl1, pitch0; |
466 | u32 lbm_size, tiling; | |
21af94cf | 467 | unsigned long irqflags; |
e065a8dd | 468 | u32 hvs_format = format->hvs; |
fc04023f | 469 | int ret, i; |
5c679994 EA |
470 | |
471 | ret = vc4_plane_setup_clipping_and_scaling(state); | |
472 | if (ret) | |
473 | return ret; | |
474 | ||
21af94cf EA |
475 | /* Allocate the LBM memory that the HVS will use for temporary |
476 | * storage due to our scaling/format conversion. | |
477 | */ | |
478 | lbm_size = vc4_lbm_size(state); | |
479 | if (lbm_size) { | |
480 | if (!vc4_state->lbm.allocated) { | |
481 | spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); | |
4e64e553 CW |
482 | ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm, |
483 | &vc4_state->lbm, | |
484 | lbm_size, 32, 0, 0); | |
21af94cf EA |
485 | spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); |
486 | } else { | |
487 | WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); | |
488 | } | |
489 | } | |
490 | ||
491 | if (ret) | |
492 | return ret; | |
493 | ||
fc04023f EA |
494 | /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB |
495 | * and 4:4:4, scl1 should be set to scl0 so both channels of | |
496 | * the scaler do the same thing. For YUV, the Y plane needs | |
497 | * to be put in channel 1 and Cb/Cr in channel 0, so we swap | |
498 | * the scl fields here. | |
499 | */ | |
500 | if (num_planes == 1) { | |
9a0e9802 | 501 | scl0 = vc4_get_scl_field(state, 0); |
fc04023f EA |
502 | scl1 = scl0; |
503 | } else { | |
504 | scl0 = vc4_get_scl_field(state, 1); | |
505 | scl1 = vc4_get_scl_field(state, 0); | |
506 | } | |
21af94cf | 507 | |
a65511b1 BB |
508 | h_subsample = drm_format_horz_chroma_subsampling(format->drm); |
509 | v_subsample = drm_format_vert_chroma_subsampling(format->drm); | |
510 | ||
e065a8dd | 511 | switch (base_format_mod) { |
98830d91 EA |
512 | case DRM_FORMAT_MOD_LINEAR: |
513 | tiling = SCALER_CTL0_TILING_LINEAR; | |
514 | pitch0 = VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH); | |
a65511b1 BB |
515 | |
516 | /* Adjust the base pointer to the first pixel to be scanned | |
517 | * out. | |
518 | */ | |
519 | for (i = 0; i < num_planes; i++) { | |
520 | vc4_state->offsets[i] += vc4_state->src_y / | |
521 | (i ? v_subsample : 1) * | |
522 | fb->pitches[i]; | |
523 | vc4_state->offsets[i] += vc4_state->src_x / | |
524 | (i ? h_subsample : 1) * | |
525 | fb->format->cpp[i]; | |
526 | } | |
98830d91 | 527 | break; |
652badb9 EA |
528 | |
529 | case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: { | |
530 | /* For T-tiled, the FB pitch is "how many bytes from | |
531 | * one row to the next, such that pitch * tile_h == | |
532 | * tile_size * tiles_per_row." | |
533 | */ | |
534 | u32 tile_size_shift = 12; /* T tiles are 4kb */ | |
535 | u32 tile_h_shift = 5; /* 16 and 32bpp are 32 pixels high */ | |
536 | u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift); | |
537 | ||
98830d91 EA |
538 | tiling = SCALER_CTL0_TILING_256B_OR_T; |
539 | ||
652badb9 EA |
540 | pitch0 = (VC4_SET_FIELD(0, SCALER_PITCH0_TILE_Y_OFFSET) | |
541 | VC4_SET_FIELD(0, SCALER_PITCH0_TILE_WIDTH_L) | | |
542 | VC4_SET_FIELD(tiles_w, SCALER_PITCH0_TILE_WIDTH_R)); | |
98830d91 | 543 | break; |
652badb9 EA |
544 | } |
545 | ||
e065a8dd DS |
546 | case DRM_FORMAT_MOD_BROADCOM_SAND64: |
547 | case DRM_FORMAT_MOD_BROADCOM_SAND128: | |
548 | case DRM_FORMAT_MOD_BROADCOM_SAND256: { | |
549 | uint32_t param = fourcc_mod_broadcom_param(fb->modifier); | |
550 | ||
551 | /* Column-based NV12 or RGBA. | |
552 | */ | |
553 | if (fb->format->num_planes > 1) { | |
554 | if (hvs_format != HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE) { | |
555 | DRM_DEBUG_KMS("SAND format only valid for NV12/21"); | |
556 | return -EINVAL; | |
557 | } | |
558 | hvs_format = HVS_PIXEL_FORMAT_H264; | |
559 | } else { | |
560 | if (base_format_mod == DRM_FORMAT_MOD_BROADCOM_SAND256) { | |
561 | DRM_DEBUG_KMS("SAND256 format only valid for H.264"); | |
562 | return -EINVAL; | |
563 | } | |
564 | } | |
565 | ||
566 | switch (base_format_mod) { | |
567 | case DRM_FORMAT_MOD_BROADCOM_SAND64: | |
568 | tiling = SCALER_CTL0_TILING_64B; | |
569 | break; | |
570 | case DRM_FORMAT_MOD_BROADCOM_SAND128: | |
571 | tiling = SCALER_CTL0_TILING_128B; | |
572 | break; | |
573 | case DRM_FORMAT_MOD_BROADCOM_SAND256: | |
574 | tiling = SCALER_CTL0_TILING_256B_OR_T; | |
575 | break; | |
576 | default: | |
577 | break; | |
578 | } | |
579 | ||
580 | if (param > SCALER_TILE_HEIGHT_MASK) { | |
581 | DRM_DEBUG_KMS("SAND height too large (%d)\n", param); | |
582 | return -EINVAL; | |
583 | } | |
584 | ||
585 | pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT); | |
586 | break; | |
587 | } | |
588 | ||
98830d91 EA |
589 | default: |
590 | DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx", | |
591 | (long long)fb->modifier); | |
592 | return -EINVAL; | |
593 | } | |
594 | ||
21af94cf | 595 | /* Control word */ |
c8b75bca EA |
596 | vc4_dlist_write(vc4_state, |
597 | SCALER_CTL0_VALID | | |
3257ec79 | 598 | VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) | |
c8b75bca | 599 | (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | |
e065a8dd | 600 | (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | |
98830d91 | 601 | VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) | |
21af94cf | 602 | (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) | |
fc04023f EA |
603 | VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) | |
604 | VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1)); | |
c8b75bca EA |
605 | |
606 | /* Position Word 0: Image Positions and Alpha Value */ | |
6674a904 | 607 | vc4_state->pos0_offset = vc4_state->dlist_count; |
c8b75bca | 608 | vc4_dlist_write(vc4_state, |
22445f03 | 609 | VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) | |
5c679994 EA |
610 | VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | |
611 | VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); | |
c8b75bca | 612 | |
21af94cf EA |
613 | /* Position Word 1: Scaled Image Dimensions. */ |
614 | if (!vc4_state->is_unity) { | |
615 | vc4_dlist_write(vc4_state, | |
616 | VC4_SET_FIELD(vc4_state->crtc_w, | |
617 | SCALER_POS1_SCL_WIDTH) | | |
618 | VC4_SET_FIELD(vc4_state->crtc_h, | |
619 | SCALER_POS1_SCL_HEIGHT)); | |
620 | } | |
c8b75bca | 621 | |
22445f03 SS |
622 | /* Don't waste cycles mixing with plane alpha if the set alpha |
623 | * is opaque or there is no per-pixel alpha information. | |
624 | * In any case we use the alpha property value as the fixed alpha. | |
625 | */ | |
626 | mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE && | |
627 | fb->format->has_alpha; | |
628 | ||
05202c24 | 629 | /* Position Word 2: Source Image Size, Alpha */ |
6674a904 | 630 | vc4_state->pos2_offset = vc4_state->dlist_count; |
c8b75bca | 631 | vc4_dlist_write(vc4_state, |
124e5dac | 632 | VC4_SET_FIELD(fb->format->has_alpha ? |
c8b75bca EA |
633 | SCALER_POS2_ALPHA_MODE_PIPELINE : |
634 | SCALER_POS2_ALPHA_MODE_FIXED, | |
635 | SCALER_POS2_ALPHA_MODE) | | |
22445f03 | 636 | (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) | |
05202c24 | 637 | (fb->format->has_alpha ? SCALER_POS2_ALPHA_PREMULT : 0) | |
fc04023f EA |
638 | VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) | |
639 | VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT)); | |
c8b75bca EA |
640 | |
641 | /* Position Word 3: Context. Written by the HVS. */ | |
642 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); | |
643 | ||
fc04023f EA |
644 | |
645 | /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers | |
646 | * | |
647 | * The pointers may be any byte address. | |
648 | */ | |
6674a904 | 649 | vc4_state->ptr0_offset = vc4_state->dlist_count; |
090cb0c6 DS |
650 | for (i = 0; i < num_planes; i++) |
651 | vc4_dlist_write(vc4_state, vc4_state->offsets[i]); | |
c8b75bca | 652 | |
fc04023f EA |
653 | /* Pointer Context Word 0/1/2: Written by the HVS */ |
654 | for (i = 0; i < num_planes; i++) | |
655 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); | |
c8b75bca | 656 | |
98830d91 EA |
657 | /* Pitch word 0 */ |
658 | vc4_dlist_write(vc4_state, pitch0); | |
659 | ||
660 | /* Pitch word 1/2 */ | |
661 | for (i = 1; i < num_planes; i++) { | |
e065a8dd DS |
662 | if (hvs_format != HVS_PIXEL_FORMAT_H264) { |
663 | vc4_dlist_write(vc4_state, | |
664 | VC4_SET_FIELD(fb->pitches[i], | |
665 | SCALER_SRC_PITCH)); | |
666 | } else { | |
667 | vc4_dlist_write(vc4_state, pitch0); | |
668 | } | |
fc04023f EA |
669 | } |
670 | ||
671 | /* Colorspace conversion words */ | |
672 | if (vc4_state->is_yuv) { | |
673 | vc4_dlist_write(vc4_state, SCALER_CSC0_ITR_R_601_5); | |
674 | vc4_dlist_write(vc4_state, SCALER_CSC1_ITR_R_601_5); | |
675 | vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5); | |
676 | } | |
c8b75bca | 677 | |
658d8cbd BB |
678 | if (vc4_state->x_scaling[0] != VC4_SCALING_NONE || |
679 | vc4_state->x_scaling[1] != VC4_SCALING_NONE || | |
680 | vc4_state->y_scaling[0] != VC4_SCALING_NONE || | |
681 | vc4_state->y_scaling[1] != VC4_SCALING_NONE) { | |
21af94cf | 682 | /* LBM Base Address. */ |
fc04023f EA |
683 | if (vc4_state->y_scaling[0] != VC4_SCALING_NONE || |
684 | vc4_state->y_scaling[1] != VC4_SCALING_NONE) { | |
21af94cf | 685 | vc4_dlist_write(vc4_state, vc4_state->lbm.start); |
fc04023f | 686 | } |
21af94cf | 687 | |
fc04023f EA |
688 | if (num_planes > 1) { |
689 | /* Emit Cb/Cr as channel 0 and Y as channel | |
690 | * 1. This matches how we set up scl0/scl1 | |
691 | * above. | |
692 | */ | |
693 | vc4_write_scaling_parameters(state, 1); | |
694 | } | |
695 | vc4_write_scaling_parameters(state, 0); | |
21af94cf EA |
696 | |
697 | /* If any PPF setup was done, then all the kernel | |
698 | * pointers get uploaded. | |
699 | */ | |
fc04023f EA |
700 | if (vc4_state->x_scaling[0] == VC4_SCALING_PPF || |
701 | vc4_state->y_scaling[0] == VC4_SCALING_PPF || | |
702 | vc4_state->x_scaling[1] == VC4_SCALING_PPF || | |
703 | vc4_state->y_scaling[1] == VC4_SCALING_PPF) { | |
21af94cf EA |
704 | u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start, |
705 | SCALER_PPF_KERNEL_OFFSET); | |
706 | ||
707 | /* HPPF plane 0 */ | |
708 | vc4_dlist_write(vc4_state, kernel); | |
709 | /* VPPF plane 0 */ | |
710 | vc4_dlist_write(vc4_state, kernel); | |
711 | /* HPPF plane 1 */ | |
712 | vc4_dlist_write(vc4_state, kernel); | |
713 | /* VPPF plane 1 */ | |
714 | vc4_dlist_write(vc4_state, kernel); | |
715 | } | |
716 | } | |
717 | ||
c8b75bca EA |
718 | vc4_state->dlist[ctl0_offset] |= |
719 | VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE); | |
720 | ||
3d67b68a SS |
721 | /* crtc_* are already clipped coordinates. */ |
722 | covers_screen = vc4_state->crtc_x == 0 && vc4_state->crtc_y == 0 && | |
723 | vc4_state->crtc_w == state->crtc->mode.hdisplay && | |
724 | vc4_state->crtc_h == state->crtc->mode.vdisplay; | |
725 | /* Background fill might be necessary when the plane has per-pixel | |
22445f03 SS |
726 | * alpha content or a non-opaque plane alpha and could blend from the |
727 | * background or does not cover the entire screen. | |
3d67b68a | 728 | */ |
22445f03 SS |
729 | vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen || |
730 | state->alpha != DRM_BLEND_ALPHA_OPAQUE; | |
3d67b68a | 731 | |
c8b75bca EA |
732 | return 0; |
733 | } | |
734 | ||
735 | /* If a modeset involves changing the setup of a plane, the atomic | |
736 | * infrastructure will call this to validate a proposed plane setup. | |
737 | * However, if a plane isn't getting updated, this (and the | |
738 | * corresponding vc4_plane_atomic_update) won't get called. Thus, we | |
739 | * compute the dlist here and have all active plane dlists get updated | |
740 | * in the CRTC's flush. | |
741 | */ | |
742 | static int vc4_plane_atomic_check(struct drm_plane *plane, | |
743 | struct drm_plane_state *state) | |
744 | { | |
745 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | |
746 | ||
747 | vc4_state->dlist_count = 0; | |
748 | ||
749 | if (plane_enabled(state)) | |
750 | return vc4_plane_mode_set(plane, state); | |
751 | else | |
752 | return 0; | |
753 | } | |
754 | ||
755 | static void vc4_plane_atomic_update(struct drm_plane *plane, | |
756 | struct drm_plane_state *old_state) | |
757 | { | |
758 | /* No contents here. Since we don't know where in the CRTC's | |
759 | * dlist we should be stored, our dlist is uploaded to the | |
760 | * hardware with vc4_plane_write_dlist() at CRTC atomic_flush | |
761 | * time. | |
762 | */ | |
763 | } | |
764 | ||
765 | u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) | |
766 | { | |
767 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); | |
768 | int i; | |
769 | ||
b501bacc EA |
770 | vc4_state->hw_dlist = dlist; |
771 | ||
c8b75bca EA |
772 | /* Can't memcpy_toio() because it needs to be 32-bit writes. */ |
773 | for (i = 0; i < vc4_state->dlist_count; i++) | |
774 | writel(vc4_state->dlist[i], &dlist[i]); | |
775 | ||
776 | return vc4_state->dlist_count; | |
777 | } | |
778 | ||
2f196b7c | 779 | u32 vc4_plane_dlist_size(const struct drm_plane_state *state) |
c8b75bca | 780 | { |
2f196b7c DV |
781 | const struct vc4_plane_state *vc4_state = |
782 | container_of(state, typeof(*vc4_state), base); | |
c8b75bca EA |
783 | |
784 | return vc4_state->dlist_count; | |
785 | } | |
786 | ||
b501bacc EA |
787 | /* Updates the plane to immediately (well, once the FIFO needs |
788 | * refilling) scan out from at a new framebuffer. | |
789 | */ | |
790 | void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) | |
791 | { | |
792 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); | |
793 | struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); | |
794 | uint32_t addr; | |
795 | ||
796 | /* We're skipping the address adjustment for negative origin, | |
797 | * because this is only called on the primary plane. | |
798 | */ | |
799 | WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0); | |
800 | addr = bo->paddr + fb->offsets[0]; | |
801 | ||
802 | /* Write the new address into the hardware immediately. The | |
803 | * scanout will start from this address as soon as the FIFO | |
804 | * needs to refill with pixels. | |
805 | */ | |
6674a904 | 806 | writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]); |
b501bacc EA |
807 | |
808 | /* Also update the CPU-side dlist copy, so that any later | |
809 | * atomic updates that don't do a new modeset on our plane | |
810 | * also use our updated address. | |
811 | */ | |
6674a904 | 812 | vc4_state->dlist[vc4_state->ptr0_offset] = addr; |
b501bacc EA |
813 | } |
814 | ||
539c320b GP |
815 | static void vc4_plane_atomic_async_update(struct drm_plane *plane, |
816 | struct drm_plane_state *state) | |
817 | { | |
818 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); | |
819 | ||
820 | if (plane->state->fb != state->fb) { | |
821 | vc4_plane_async_set_fb(plane, state->fb); | |
822 | drm_atomic_set_fb_for_plane(plane->state, state->fb); | |
823 | } | |
824 | ||
825 | /* Set the cursor's position on the screen. This is the | |
826 | * expected change from the drm_mode_cursor_universal() | |
827 | * helper. | |
828 | */ | |
829 | plane->state->crtc_x = state->crtc_x; | |
830 | plane->state->crtc_y = state->crtc_y; | |
831 | ||
832 | /* Allow changing the start position within the cursor BO, if | |
833 | * that matters. | |
834 | */ | |
835 | plane->state->src_x = state->src_x; | |
836 | plane->state->src_y = state->src_y; | |
837 | ||
838 | /* Update the display list based on the new crtc_x/y. */ | |
839 | vc4_plane_atomic_check(plane, plane->state); | |
840 | ||
841 | /* Note that we can't just call vc4_plane_write_dlist() | |
842 | * because that would smash the context data that the HVS is | |
843 | * currently using. | |
844 | */ | |
845 | writel(vc4_state->dlist[vc4_state->pos0_offset], | |
846 | &vc4_state->hw_dlist[vc4_state->pos0_offset]); | |
847 | writel(vc4_state->dlist[vc4_state->pos2_offset], | |
848 | &vc4_state->hw_dlist[vc4_state->pos2_offset]); | |
849 | writel(vc4_state->dlist[vc4_state->ptr0_offset], | |
850 | &vc4_state->hw_dlist[vc4_state->ptr0_offset]); | |
851 | } | |
852 | ||
853 | static int vc4_plane_atomic_async_check(struct drm_plane *plane, | |
854 | struct drm_plane_state *state) | |
855 | { | |
856 | /* No configuring new scaling in the fast path. */ | |
857 | if (plane->state->crtc_w != state->crtc_w || | |
858 | plane->state->crtc_h != state->crtc_h || | |
859 | plane->state->src_w != state->src_w || | |
860 | plane->state->src_h != state->src_h) | |
861 | return -EINVAL; | |
862 | ||
863 | return 0; | |
864 | } | |
865 | ||
334dbd69 EA |
866 | static int vc4_prepare_fb(struct drm_plane *plane, |
867 | struct drm_plane_state *state) | |
868 | { | |
869 | struct vc4_bo *bo; | |
870 | struct dma_fence *fence; | |
b9f19259 | 871 | int ret; |
334dbd69 | 872 | |
2227a7a2 | 873 | if (!state->fb) |
334dbd69 EA |
874 | return 0; |
875 | ||
876 | bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); | |
b9f19259 | 877 | |
2227a7a2 DV |
878 | fence = reservation_object_get_excl_rcu(bo->resv); |
879 | drm_atomic_set_fence_for_plane(state, fence); | |
880 | ||
881 | if (plane->state->fb == state->fb) | |
882 | return 0; | |
883 | ||
b9f19259 BB |
884 | ret = vc4_bo_inc_usecnt(bo); |
885 | if (ret) | |
886 | return ret; | |
887 | ||
334dbd69 EA |
888 | return 0; |
889 | } | |
890 | ||
b9f19259 BB |
891 | static void vc4_cleanup_fb(struct drm_plane *plane, |
892 | struct drm_plane_state *state) | |
893 | { | |
894 | struct vc4_bo *bo; | |
895 | ||
896 | if (plane->state->fb == state->fb || !state->fb) | |
897 | return; | |
898 | ||
899 | bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); | |
900 | vc4_bo_dec_usecnt(bo); | |
901 | } | |
902 | ||
c8b75bca | 903 | static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { |
c8b75bca EA |
904 | .atomic_check = vc4_plane_atomic_check, |
905 | .atomic_update = vc4_plane_atomic_update, | |
334dbd69 | 906 | .prepare_fb = vc4_prepare_fb, |
b9f19259 | 907 | .cleanup_fb = vc4_cleanup_fb, |
539c320b GP |
908 | .atomic_async_check = vc4_plane_atomic_async_check, |
909 | .atomic_async_update = vc4_plane_atomic_async_update, | |
c8b75bca EA |
910 | }; |
911 | ||
912 | static void vc4_plane_destroy(struct drm_plane *plane) | |
913 | { | |
c8b75bca EA |
914 | drm_plane_cleanup(plane); |
915 | } | |
916 | ||
423ad7b3 DS |
917 | static bool vc4_format_mod_supported(struct drm_plane *plane, |
918 | uint32_t format, | |
919 | uint64_t modifier) | |
920 | { | |
921 | /* Support T_TILING for RGB formats only. */ | |
922 | switch (format) { | |
923 | case DRM_FORMAT_XRGB8888: | |
924 | case DRM_FORMAT_ARGB8888: | |
925 | case DRM_FORMAT_ABGR8888: | |
926 | case DRM_FORMAT_XBGR8888: | |
927 | case DRM_FORMAT_RGB565: | |
928 | case DRM_FORMAT_BGR565: | |
929 | case DRM_FORMAT_ARGB1555: | |
930 | case DRM_FORMAT_XRGB1555: | |
e065a8dd DS |
931 | switch (fourcc_mod_broadcom_mod(modifier)) { |
932 | case DRM_FORMAT_MOD_LINEAR: | |
933 | case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: | |
934 | case DRM_FORMAT_MOD_BROADCOM_SAND64: | |
935 | case DRM_FORMAT_MOD_BROADCOM_SAND128: | |
936 | return true; | |
937 | default: | |
938 | return false; | |
939 | } | |
940 | case DRM_FORMAT_NV12: | |
941 | case DRM_FORMAT_NV21: | |
942 | switch (fourcc_mod_broadcom_mod(modifier)) { | |
943 | case DRM_FORMAT_MOD_LINEAR: | |
944 | case DRM_FORMAT_MOD_BROADCOM_SAND64: | |
945 | case DRM_FORMAT_MOD_BROADCOM_SAND128: | |
946 | case DRM_FORMAT_MOD_BROADCOM_SAND256: | |
947 | return true; | |
948 | default: | |
949 | return false; | |
950 | } | |
423ad7b3 DS |
951 | case DRM_FORMAT_YUV422: |
952 | case DRM_FORMAT_YVU422: | |
953 | case DRM_FORMAT_YUV420: | |
954 | case DRM_FORMAT_YVU420: | |
423ad7b3 | 955 | case DRM_FORMAT_NV16: |
1e871d65 | 956 | case DRM_FORMAT_NV61: |
423ad7b3 DS |
957 | default: |
958 | return (modifier == DRM_FORMAT_MOD_LINEAR); | |
959 | } | |
960 | } | |
961 | ||
c8b75bca | 962 | static const struct drm_plane_funcs vc4_plane_funcs = { |
539c320b | 963 | .update_plane = drm_atomic_helper_update_plane, |
c8b75bca EA |
964 | .disable_plane = drm_atomic_helper_disable_plane, |
965 | .destroy = vc4_plane_destroy, | |
966 | .set_property = NULL, | |
967 | .reset = vc4_plane_reset, | |
968 | .atomic_duplicate_state = vc4_plane_duplicate_state, | |
969 | .atomic_destroy_state = vc4_plane_destroy_state, | |
423ad7b3 | 970 | .format_mod_supported = vc4_format_mod_supported, |
c8b75bca EA |
971 | }; |
972 | ||
973 | struct drm_plane *vc4_plane_init(struct drm_device *dev, | |
974 | enum drm_plane_type type) | |
975 | { | |
976 | struct drm_plane *plane = NULL; | |
977 | struct vc4_plane *vc4_plane; | |
978 | u32 formats[ARRAY_SIZE(hvs_formats)]; | |
fc04023f | 979 | u32 num_formats = 0; |
c8b75bca EA |
980 | int ret = 0; |
981 | unsigned i; | |
423ad7b3 DS |
982 | static const uint64_t modifiers[] = { |
983 | DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED, | |
e065a8dd DS |
984 | DRM_FORMAT_MOD_BROADCOM_SAND128, |
985 | DRM_FORMAT_MOD_BROADCOM_SAND64, | |
986 | DRM_FORMAT_MOD_BROADCOM_SAND256, | |
423ad7b3 DS |
987 | DRM_FORMAT_MOD_LINEAR, |
988 | DRM_FORMAT_MOD_INVALID | |
989 | }; | |
c8b75bca EA |
990 | |
991 | vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane), | |
992 | GFP_KERNEL); | |
7b347348 CIK |
993 | if (!vc4_plane) |
994 | return ERR_PTR(-ENOMEM); | |
c8b75bca | 995 | |
fc04023f EA |
996 | for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { |
997 | /* Don't allow YUV in cursor planes, since that means | |
998 | * tuning on the scaler, which we don't allow for the | |
999 | * cursor. | |
1000 | */ | |
1001 | if (type != DRM_PLANE_TYPE_CURSOR || | |
1002 | hvs_formats[i].hvs < HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE) { | |
1003 | formats[num_formats++] = hvs_formats[i].drm; | |
1004 | } | |
1005 | } | |
c8b75bca | 1006 | plane = &vc4_plane->base; |
49d29a07 | 1007 | ret = drm_universal_plane_init(dev, plane, 0, |
c8b75bca | 1008 | &vc4_plane_funcs, |
fc04023f | 1009 | formats, num_formats, |
423ad7b3 | 1010 | modifiers, type, NULL); |
c8b75bca EA |
1011 | |
1012 | drm_plane_helper_add(plane, &vc4_plane_helper_funcs); | |
1013 | ||
22445f03 SS |
1014 | drm_plane_create_alpha_property(plane); |
1015 | ||
c8b75bca | 1016 | return plane; |
c8b75bca | 1017 | } |