]>
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; |
fc04023f EA |
269 | u32 h_subsample = 1; |
270 | u32 v_subsample = 1; | |
271 | int i; | |
5c679994 | 272 | |
fc04023f EA |
273 | for (i = 0; i < num_planes; i++) |
274 | vc4_state->offsets[i] = bo->paddr + fb->offsets[i]; | |
5c679994 | 275 | |
21af94cf EA |
276 | /* We don't support subpixel source positioning for scaling. */ |
277 | if ((state->src_x & subpixel_src_mask) || | |
278 | (state->src_y & subpixel_src_mask) || | |
279 | (state->src_w & subpixel_src_mask) || | |
280 | (state->src_h & subpixel_src_mask)) { | |
bf893acc EA |
281 | return -EINVAL; |
282 | } | |
283 | ||
21af94cf EA |
284 | vc4_state->src_x = state->src_x >> 16; |
285 | vc4_state->src_y = state->src_y >> 16; | |
fc04023f EA |
286 | vc4_state->src_w[0] = state->src_w >> 16; |
287 | vc4_state->src_h[0] = state->src_h >> 16; | |
f863e356 EA |
288 | |
289 | vc4_state->crtc_x = state->crtc_x; | |
290 | vc4_state->crtc_y = state->crtc_y; | |
291 | vc4_state->crtc_w = state->crtc_w; | |
292 | vc4_state->crtc_h = state->crtc_h; | |
293 | ||
fc04023f EA |
294 | vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], |
295 | vc4_state->crtc_w); | |
296 | vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], | |
297 | vc4_state->crtc_h); | |
298 | ||
658d8cbd BB |
299 | vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE && |
300 | vc4_state->y_scaling[0] == VC4_SCALING_NONE); | |
301 | ||
fc04023f EA |
302 | if (num_planes > 1) { |
303 | vc4_state->is_yuv = true; | |
304 | ||
305 | h_subsample = drm_format_horz_chroma_subsampling(format); | |
306 | v_subsample = drm_format_vert_chroma_subsampling(format); | |
307 | vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample; | |
308 | vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample; | |
309 | ||
310 | vc4_state->x_scaling[1] = | |
311 | vc4_get_scaling_mode(vc4_state->src_w[1], | |
312 | vc4_state->crtc_w); | |
313 | vc4_state->y_scaling[1] = | |
314 | vc4_get_scaling_mode(vc4_state->src_h[1], | |
315 | vc4_state->crtc_h); | |
316 | ||
658d8cbd BB |
317 | /* YUV conversion requires that horizontal scaling be enabled, |
318 | * even on a plane that's otherwise 1:1. Looks like only PPF | |
319 | * works in that case, so let's pick that one. | |
fc04023f | 320 | */ |
658d8cbd BB |
321 | if (vc4_state->is_unity) |
322 | vc4_state->x_scaling[0] = VC4_SCALING_PPF; | |
a6a00918 BB |
323 | } else { |
324 | vc4_state->x_scaling[1] = VC4_SCALING_NONE; | |
325 | vc4_state->y_scaling[1] = VC4_SCALING_NONE; | |
fc04023f EA |
326 | } |
327 | ||
21af94cf EA |
328 | /* No configuring scaling on the cursor plane, since it gets |
329 | non-vblank-synced updates, and scaling requires requires | |
330 | LBM changes which have to be vblank-synced. | |
331 | */ | |
332 | if (plane->type == DRM_PLANE_TYPE_CURSOR && !vc4_state->is_unity) | |
333 | return -EINVAL; | |
334 | ||
335 | /* Clamp the on-screen start x/y to 0. The hardware doesn't | |
336 | * support negative y, and negative x wastes bandwidth. | |
337 | */ | |
5c679994 | 338 | if (vc4_state->crtc_x < 0) { |
fc04023f | 339 | for (i = 0; i < num_planes; i++) { |
353c8598 | 340 | u32 cpp = fb->format->cpp[i]; |
fc04023f EA |
341 | u32 subs = ((i == 0) ? 1 : h_subsample); |
342 | ||
343 | vc4_state->offsets[i] += (cpp * | |
344 | (-vc4_state->crtc_x) / subs); | |
345 | } | |
346 | vc4_state->src_w[0] += vc4_state->crtc_x; | |
347 | vc4_state->src_w[1] += vc4_state->crtc_x / h_subsample; | |
5c679994 | 348 | vc4_state->crtc_x = 0; |
c8b75bca EA |
349 | } |
350 | ||
5c679994 | 351 | if (vc4_state->crtc_y < 0) { |
fc04023f EA |
352 | for (i = 0; i < num_planes; i++) { |
353 | u32 subs = ((i == 0) ? 1 : v_subsample); | |
354 | ||
355 | vc4_state->offsets[i] += (fb->pitches[i] * | |
356 | (-vc4_state->crtc_y) / subs); | |
357 | } | |
358 | vc4_state->src_h[0] += vc4_state->crtc_y; | |
359 | vc4_state->src_h[1] += vc4_state->crtc_y / v_subsample; | |
5c679994 | 360 | vc4_state->crtc_y = 0; |
c8b75bca EA |
361 | } |
362 | ||
5c679994 EA |
363 | return 0; |
364 | } | |
365 | ||
21af94cf EA |
366 | static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst) |
367 | { | |
368 | u32 scale, recip; | |
369 | ||
370 | scale = (1 << 16) * src / dst; | |
371 | ||
372 | /* The specs note that while the reciprocal would be defined | |
373 | * as (1<<32)/scale, ~0 is close enough. | |
374 | */ | |
375 | recip = ~0 / scale; | |
376 | ||
377 | vc4_dlist_write(vc4_state, | |
378 | VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) | | |
379 | VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE)); | |
380 | vc4_dlist_write(vc4_state, | |
381 | VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP)); | |
382 | } | |
383 | ||
384 | static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst) | |
385 | { | |
386 | u32 scale = (1 << 16) * src / dst; | |
387 | ||
388 | vc4_dlist_write(vc4_state, | |
389 | SCALER_PPF_AGC | | |
390 | VC4_SET_FIELD(scale, SCALER_PPF_SCALE) | | |
391 | VC4_SET_FIELD(0, SCALER_PPF_IPHASE)); | |
392 | } | |
393 | ||
394 | static u32 vc4_lbm_size(struct drm_plane_state *state) | |
395 | { | |
396 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | |
397 | /* This is the worst case number. One of the two sizes will | |
398 | * be used depending on the scaling configuration. | |
399 | */ | |
fc04023f | 400 | u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w); |
21af94cf EA |
401 | u32 lbm; |
402 | ||
fc04023f EA |
403 | if (!vc4_state->is_yuv) { |
404 | if (vc4_state->is_unity) | |
405 | return 0; | |
406 | else if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) | |
407 | lbm = pix_per_line * 8; | |
408 | else { | |
409 | /* In special cases, this multiplier might be 12. */ | |
410 | lbm = pix_per_line * 16; | |
411 | } | |
412 | } else { | |
413 | /* There are cases for this going down to a multiplier | |
414 | * of 2, but according to the firmware source, the | |
415 | * table in the docs is somewhat wrong. | |
416 | */ | |
21af94cf EA |
417 | lbm = pix_per_line * 16; |
418 | } | |
419 | ||
420 | lbm = roundup(lbm, 32); | |
421 | ||
422 | return lbm; | |
423 | } | |
424 | ||
fc04023f EA |
425 | static void vc4_write_scaling_parameters(struct drm_plane_state *state, |
426 | int channel) | |
21af94cf EA |
427 | { |
428 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | |
429 | ||
430 | /* Ch0 H-PPF Word 0: Scaling Parameters */ | |
fc04023f | 431 | if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) { |
21af94cf | 432 | vc4_write_ppf(vc4_state, |
fc04023f | 433 | vc4_state->src_w[channel], vc4_state->crtc_w); |
21af94cf EA |
434 | } |
435 | ||
436 | /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */ | |
fc04023f | 437 | if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) { |
21af94cf | 438 | vc4_write_ppf(vc4_state, |
fc04023f | 439 | vc4_state->src_h[channel], vc4_state->crtc_h); |
21af94cf EA |
440 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); |
441 | } | |
442 | ||
443 | /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */ | |
fc04023f | 444 | if (vc4_state->x_scaling[channel] == VC4_SCALING_TPZ) { |
21af94cf | 445 | vc4_write_tpz(vc4_state, |
fc04023f | 446 | vc4_state->src_w[channel], vc4_state->crtc_w); |
21af94cf EA |
447 | } |
448 | ||
449 | /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */ | |
fc04023f | 450 | if (vc4_state->y_scaling[channel] == VC4_SCALING_TPZ) { |
21af94cf | 451 | vc4_write_tpz(vc4_state, |
fc04023f | 452 | vc4_state->src_h[channel], vc4_state->crtc_h); |
21af94cf EA |
453 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); |
454 | } | |
455 | } | |
5c679994 EA |
456 | |
457 | /* Writes out a full display list for an active plane to the plane's | |
458 | * private dlist state. | |
459 | */ | |
460 | static int vc4_plane_mode_set(struct drm_plane *plane, | |
461 | struct drm_plane_state *state) | |
462 | { | |
21af94cf | 463 | struct vc4_dev *vc4 = to_vc4_dev(plane->dev); |
5c679994 EA |
464 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
465 | struct drm_framebuffer *fb = state->fb; | |
5c679994 | 466 | u32 ctl0_offset = vc4_state->dlist_count; |
438b74a5 | 467 | const struct hvs_format *format = vc4_get_hvs_format(fb->format->format); |
e065a8dd | 468 | u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier); |
fc04023f | 469 | int num_planes = drm_format_num_planes(format->drm); |
22445f03 | 470 | bool mix_plane_alpha; |
3d67b68a | 471 | bool covers_screen; |
98830d91 EA |
472 | u32 scl0, scl1, pitch0; |
473 | u32 lbm_size, tiling; | |
21af94cf | 474 | unsigned long irqflags; |
e065a8dd | 475 | u32 hvs_format = format->hvs; |
fc04023f | 476 | int ret, i; |
5c679994 EA |
477 | |
478 | ret = vc4_plane_setup_clipping_and_scaling(state); | |
479 | if (ret) | |
480 | return ret; | |
481 | ||
21af94cf EA |
482 | /* Allocate the LBM memory that the HVS will use for temporary |
483 | * storage due to our scaling/format conversion. | |
484 | */ | |
485 | lbm_size = vc4_lbm_size(state); | |
486 | if (lbm_size) { | |
487 | if (!vc4_state->lbm.allocated) { | |
488 | spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); | |
4e64e553 CW |
489 | ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm, |
490 | &vc4_state->lbm, | |
491 | lbm_size, 32, 0, 0); | |
21af94cf EA |
492 | spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); |
493 | } else { | |
494 | WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); | |
495 | } | |
496 | } | |
497 | ||
498 | if (ret) | |
499 | return ret; | |
500 | ||
fc04023f EA |
501 | /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB |
502 | * and 4:4:4, scl1 should be set to scl0 so both channels of | |
503 | * the scaler do the same thing. For YUV, the Y plane needs | |
504 | * to be put in channel 1 and Cb/Cr in channel 0, so we swap | |
505 | * the scl fields here. | |
506 | */ | |
507 | if (num_planes == 1) { | |
9a0e9802 | 508 | scl0 = vc4_get_scl_field(state, 0); |
fc04023f EA |
509 | scl1 = scl0; |
510 | } else { | |
511 | scl0 = vc4_get_scl_field(state, 1); | |
512 | scl1 = vc4_get_scl_field(state, 0); | |
513 | } | |
21af94cf | 514 | |
e065a8dd | 515 | switch (base_format_mod) { |
98830d91 EA |
516 | case DRM_FORMAT_MOD_LINEAR: |
517 | tiling = SCALER_CTL0_TILING_LINEAR; | |
518 | pitch0 = VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH); | |
519 | break; | |
652badb9 EA |
520 | |
521 | case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: { | |
522 | /* For T-tiled, the FB pitch is "how many bytes from | |
523 | * one row to the next, such that pitch * tile_h == | |
524 | * tile_size * tiles_per_row." | |
525 | */ | |
526 | u32 tile_size_shift = 12; /* T tiles are 4kb */ | |
527 | u32 tile_h_shift = 5; /* 16 and 32bpp are 32 pixels high */ | |
528 | u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift); | |
529 | ||
98830d91 EA |
530 | tiling = SCALER_CTL0_TILING_256B_OR_T; |
531 | ||
652badb9 EA |
532 | pitch0 = (VC4_SET_FIELD(0, SCALER_PITCH0_TILE_Y_OFFSET) | |
533 | VC4_SET_FIELD(0, SCALER_PITCH0_TILE_WIDTH_L) | | |
534 | VC4_SET_FIELD(tiles_w, SCALER_PITCH0_TILE_WIDTH_R)); | |
98830d91 | 535 | break; |
652badb9 EA |
536 | } |
537 | ||
e065a8dd DS |
538 | case DRM_FORMAT_MOD_BROADCOM_SAND64: |
539 | case DRM_FORMAT_MOD_BROADCOM_SAND128: | |
540 | case DRM_FORMAT_MOD_BROADCOM_SAND256: { | |
541 | uint32_t param = fourcc_mod_broadcom_param(fb->modifier); | |
542 | ||
543 | /* Column-based NV12 or RGBA. | |
544 | */ | |
545 | if (fb->format->num_planes > 1) { | |
546 | if (hvs_format != HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE) { | |
547 | DRM_DEBUG_KMS("SAND format only valid for NV12/21"); | |
548 | return -EINVAL; | |
549 | } | |
550 | hvs_format = HVS_PIXEL_FORMAT_H264; | |
551 | } else { | |
552 | if (base_format_mod == DRM_FORMAT_MOD_BROADCOM_SAND256) { | |
553 | DRM_DEBUG_KMS("SAND256 format only valid for H.264"); | |
554 | return -EINVAL; | |
555 | } | |
556 | } | |
557 | ||
558 | switch (base_format_mod) { | |
559 | case DRM_FORMAT_MOD_BROADCOM_SAND64: | |
560 | tiling = SCALER_CTL0_TILING_64B; | |
561 | break; | |
562 | case DRM_FORMAT_MOD_BROADCOM_SAND128: | |
563 | tiling = SCALER_CTL0_TILING_128B; | |
564 | break; | |
565 | case DRM_FORMAT_MOD_BROADCOM_SAND256: | |
566 | tiling = SCALER_CTL0_TILING_256B_OR_T; | |
567 | break; | |
568 | default: | |
569 | break; | |
570 | } | |
571 | ||
572 | if (param > SCALER_TILE_HEIGHT_MASK) { | |
573 | DRM_DEBUG_KMS("SAND height too large (%d)\n", param); | |
574 | return -EINVAL; | |
575 | } | |
576 | ||
577 | pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT); | |
578 | break; | |
579 | } | |
580 | ||
98830d91 EA |
581 | default: |
582 | DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx", | |
583 | (long long)fb->modifier); | |
584 | return -EINVAL; | |
585 | } | |
586 | ||
21af94cf | 587 | /* Control word */ |
c8b75bca EA |
588 | vc4_dlist_write(vc4_state, |
589 | SCALER_CTL0_VALID | | |
3257ec79 | 590 | VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) | |
c8b75bca | 591 | (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | |
e065a8dd | 592 | (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | |
98830d91 | 593 | VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) | |
21af94cf | 594 | (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) | |
fc04023f EA |
595 | VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) | |
596 | VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1)); | |
c8b75bca EA |
597 | |
598 | /* Position Word 0: Image Positions and Alpha Value */ | |
6674a904 | 599 | vc4_state->pos0_offset = vc4_state->dlist_count; |
c8b75bca | 600 | vc4_dlist_write(vc4_state, |
22445f03 | 601 | VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) | |
5c679994 EA |
602 | VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) | |
603 | VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y)); | |
c8b75bca | 604 | |
21af94cf EA |
605 | /* Position Word 1: Scaled Image Dimensions. */ |
606 | if (!vc4_state->is_unity) { | |
607 | vc4_dlist_write(vc4_state, | |
608 | VC4_SET_FIELD(vc4_state->crtc_w, | |
609 | SCALER_POS1_SCL_WIDTH) | | |
610 | VC4_SET_FIELD(vc4_state->crtc_h, | |
611 | SCALER_POS1_SCL_HEIGHT)); | |
612 | } | |
c8b75bca | 613 | |
22445f03 SS |
614 | /* Don't waste cycles mixing with plane alpha if the set alpha |
615 | * is opaque or there is no per-pixel alpha information. | |
616 | * In any case we use the alpha property value as the fixed alpha. | |
617 | */ | |
618 | mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE && | |
619 | fb->format->has_alpha; | |
620 | ||
05202c24 | 621 | /* Position Word 2: Source Image Size, Alpha */ |
6674a904 | 622 | vc4_state->pos2_offset = vc4_state->dlist_count; |
c8b75bca | 623 | vc4_dlist_write(vc4_state, |
124e5dac | 624 | VC4_SET_FIELD(fb->format->has_alpha ? |
c8b75bca EA |
625 | SCALER_POS2_ALPHA_MODE_PIPELINE : |
626 | SCALER_POS2_ALPHA_MODE_FIXED, | |
627 | SCALER_POS2_ALPHA_MODE) | | |
22445f03 | 628 | (mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) | |
05202c24 | 629 | (fb->format->has_alpha ? SCALER_POS2_ALPHA_PREMULT : 0) | |
fc04023f EA |
630 | VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) | |
631 | VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT)); | |
c8b75bca EA |
632 | |
633 | /* Position Word 3: Context. Written by the HVS. */ | |
634 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); | |
635 | ||
fc04023f EA |
636 | |
637 | /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers | |
638 | * | |
639 | * The pointers may be any byte address. | |
640 | */ | |
6674a904 | 641 | vc4_state->ptr0_offset = vc4_state->dlist_count; |
090cb0c6 DS |
642 | for (i = 0; i < num_planes; i++) |
643 | vc4_dlist_write(vc4_state, vc4_state->offsets[i]); | |
c8b75bca | 644 | |
fc04023f EA |
645 | /* Pointer Context Word 0/1/2: Written by the HVS */ |
646 | for (i = 0; i < num_planes; i++) | |
647 | vc4_dlist_write(vc4_state, 0xc0c0c0c0); | |
c8b75bca | 648 | |
98830d91 EA |
649 | /* Pitch word 0 */ |
650 | vc4_dlist_write(vc4_state, pitch0); | |
651 | ||
652 | /* Pitch word 1/2 */ | |
653 | for (i = 1; i < num_planes; i++) { | |
e065a8dd DS |
654 | if (hvs_format != HVS_PIXEL_FORMAT_H264) { |
655 | vc4_dlist_write(vc4_state, | |
656 | VC4_SET_FIELD(fb->pitches[i], | |
657 | SCALER_SRC_PITCH)); | |
658 | } else { | |
659 | vc4_dlist_write(vc4_state, pitch0); | |
660 | } | |
fc04023f EA |
661 | } |
662 | ||
663 | /* Colorspace conversion words */ | |
664 | if (vc4_state->is_yuv) { | |
665 | vc4_dlist_write(vc4_state, SCALER_CSC0_ITR_R_601_5); | |
666 | vc4_dlist_write(vc4_state, SCALER_CSC1_ITR_R_601_5); | |
667 | vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5); | |
668 | } | |
c8b75bca | 669 | |
658d8cbd BB |
670 | if (vc4_state->x_scaling[0] != VC4_SCALING_NONE || |
671 | vc4_state->x_scaling[1] != VC4_SCALING_NONE || | |
672 | vc4_state->y_scaling[0] != VC4_SCALING_NONE || | |
673 | vc4_state->y_scaling[1] != VC4_SCALING_NONE) { | |
21af94cf | 674 | /* LBM Base Address. */ |
fc04023f EA |
675 | if (vc4_state->y_scaling[0] != VC4_SCALING_NONE || |
676 | vc4_state->y_scaling[1] != VC4_SCALING_NONE) { | |
21af94cf | 677 | vc4_dlist_write(vc4_state, vc4_state->lbm.start); |
fc04023f | 678 | } |
21af94cf | 679 | |
fc04023f EA |
680 | if (num_planes > 1) { |
681 | /* Emit Cb/Cr as channel 0 and Y as channel | |
682 | * 1. This matches how we set up scl0/scl1 | |
683 | * above. | |
684 | */ | |
685 | vc4_write_scaling_parameters(state, 1); | |
686 | } | |
687 | vc4_write_scaling_parameters(state, 0); | |
21af94cf EA |
688 | |
689 | /* If any PPF setup was done, then all the kernel | |
690 | * pointers get uploaded. | |
691 | */ | |
fc04023f EA |
692 | if (vc4_state->x_scaling[0] == VC4_SCALING_PPF || |
693 | vc4_state->y_scaling[0] == VC4_SCALING_PPF || | |
694 | vc4_state->x_scaling[1] == VC4_SCALING_PPF || | |
695 | vc4_state->y_scaling[1] == VC4_SCALING_PPF) { | |
21af94cf EA |
696 | u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start, |
697 | SCALER_PPF_KERNEL_OFFSET); | |
698 | ||
699 | /* HPPF plane 0 */ | |
700 | vc4_dlist_write(vc4_state, kernel); | |
701 | /* VPPF plane 0 */ | |
702 | vc4_dlist_write(vc4_state, kernel); | |
703 | /* HPPF plane 1 */ | |
704 | vc4_dlist_write(vc4_state, kernel); | |
705 | /* VPPF plane 1 */ | |
706 | vc4_dlist_write(vc4_state, kernel); | |
707 | } | |
708 | } | |
709 | ||
c8b75bca EA |
710 | vc4_state->dlist[ctl0_offset] |= |
711 | VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE); | |
712 | ||
3d67b68a SS |
713 | /* crtc_* are already clipped coordinates. */ |
714 | covers_screen = vc4_state->crtc_x == 0 && vc4_state->crtc_y == 0 && | |
715 | vc4_state->crtc_w == state->crtc->mode.hdisplay && | |
716 | vc4_state->crtc_h == state->crtc->mode.vdisplay; | |
717 | /* Background fill might be necessary when the plane has per-pixel | |
22445f03 SS |
718 | * alpha content or a non-opaque plane alpha and could blend from the |
719 | * background or does not cover the entire screen. | |
3d67b68a | 720 | */ |
22445f03 SS |
721 | vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen || |
722 | state->alpha != DRM_BLEND_ALPHA_OPAQUE; | |
3d67b68a | 723 | |
c8b75bca EA |
724 | return 0; |
725 | } | |
726 | ||
727 | /* If a modeset involves changing the setup of a plane, the atomic | |
728 | * infrastructure will call this to validate a proposed plane setup. | |
729 | * However, if a plane isn't getting updated, this (and the | |
730 | * corresponding vc4_plane_atomic_update) won't get called. Thus, we | |
731 | * compute the dlist here and have all active plane dlists get updated | |
732 | * in the CRTC's flush. | |
733 | */ | |
734 | static int vc4_plane_atomic_check(struct drm_plane *plane, | |
735 | struct drm_plane_state *state) | |
736 | { | |
737 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); | |
738 | ||
739 | vc4_state->dlist_count = 0; | |
740 | ||
741 | if (plane_enabled(state)) | |
742 | return vc4_plane_mode_set(plane, state); | |
743 | else | |
744 | return 0; | |
745 | } | |
746 | ||
747 | static void vc4_plane_atomic_update(struct drm_plane *plane, | |
748 | struct drm_plane_state *old_state) | |
749 | { | |
750 | /* No contents here. Since we don't know where in the CRTC's | |
751 | * dlist we should be stored, our dlist is uploaded to the | |
752 | * hardware with vc4_plane_write_dlist() at CRTC atomic_flush | |
753 | * time. | |
754 | */ | |
755 | } | |
756 | ||
757 | u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) | |
758 | { | |
759 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); | |
760 | int i; | |
761 | ||
b501bacc EA |
762 | vc4_state->hw_dlist = dlist; |
763 | ||
c8b75bca EA |
764 | /* Can't memcpy_toio() because it needs to be 32-bit writes. */ |
765 | for (i = 0; i < vc4_state->dlist_count; i++) | |
766 | writel(vc4_state->dlist[i], &dlist[i]); | |
767 | ||
768 | return vc4_state->dlist_count; | |
769 | } | |
770 | ||
2f196b7c | 771 | u32 vc4_plane_dlist_size(const struct drm_plane_state *state) |
c8b75bca | 772 | { |
2f196b7c SV |
773 | const struct vc4_plane_state *vc4_state = |
774 | container_of(state, typeof(*vc4_state), base); | |
c8b75bca EA |
775 | |
776 | return vc4_state->dlist_count; | |
777 | } | |
778 | ||
b501bacc EA |
779 | /* Updates the plane to immediately (well, once the FIFO needs |
780 | * refilling) scan out from at a new framebuffer. | |
781 | */ | |
782 | void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) | |
783 | { | |
784 | struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); | |
785 | struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); | |
786 | uint32_t addr; | |
787 | ||
788 | /* We're skipping the address adjustment for negative origin, | |
789 | * because this is only called on the primary plane. | |
790 | */ | |
791 | WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0); | |
792 | addr = bo->paddr + fb->offsets[0]; | |
793 | ||
794 | /* Write the new address into the hardware immediately. The | |
795 | * scanout will start from this address as soon as the FIFO | |
796 | * needs to refill with pixels. | |
797 | */ | |
6674a904 | 798 | writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]); |
b501bacc EA |
799 | |
800 | /* Also update the CPU-side dlist copy, so that any later | |
801 | * atomic updates that don't do a new modeset on our plane | |
802 | * also use our updated address. | |
803 | */ | |
6674a904 | 804 | vc4_state->dlist[vc4_state->ptr0_offset] = addr; |
b501bacc EA |
805 | } |
806 | ||
539c320b GP |
807 | static void vc4_plane_atomic_async_update(struct drm_plane *plane, |
808 | struct drm_plane_state *state) | |
809 | { | |
5a43911f | 810 | struct vc4_plane_state *vc4_state, *new_vc4_state; |
539c320b GP |
811 | |
812 | if (plane->state->fb != state->fb) { | |
813 | vc4_plane_async_set_fb(plane, state->fb); | |
814 | drm_atomic_set_fb_for_plane(plane->state, state->fb); | |
815 | } | |
816 | ||
817 | /* Set the cursor's position on the screen. This is the | |
818 | * expected change from the drm_mode_cursor_universal() | |
819 | * helper. | |
820 | */ | |
821 | plane->state->crtc_x = state->crtc_x; | |
822 | plane->state->crtc_y = state->crtc_y; | |
823 | ||
824 | /* Allow changing the start position within the cursor BO, if | |
825 | * that matters. | |
826 | */ | |
827 | plane->state->src_x = state->src_x; | |
828 | plane->state->src_y = state->src_y; | |
829 | ||
830 | /* Update the display list based on the new crtc_x/y. */ | |
5a43911f BB |
831 | vc4_plane_atomic_check(plane, state); |
832 | ||
833 | new_vc4_state = to_vc4_plane_state(state); | |
834 | vc4_state = to_vc4_plane_state(plane->state); | |
835 | ||
836 | /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */ | |
837 | vc4_state->dlist[vc4_state->pos0_offset] = | |
838 | new_vc4_state->dlist[vc4_state->pos0_offset]; | |
839 | vc4_state->dlist[vc4_state->pos2_offset] = | |
840 | new_vc4_state->dlist[vc4_state->pos2_offset]; | |
841 | vc4_state->dlist[vc4_state->ptr0_offset] = | |
842 | new_vc4_state->dlist[vc4_state->ptr0_offset]; | |
539c320b GP |
843 | |
844 | /* Note that we can't just call vc4_plane_write_dlist() | |
845 | * because that would smash the context data that the HVS is | |
846 | * currently using. | |
847 | */ | |
848 | writel(vc4_state->dlist[vc4_state->pos0_offset], | |
849 | &vc4_state->hw_dlist[vc4_state->pos0_offset]); | |
850 | writel(vc4_state->dlist[vc4_state->pos2_offset], | |
851 | &vc4_state->hw_dlist[vc4_state->pos2_offset]); | |
852 | writel(vc4_state->dlist[vc4_state->ptr0_offset], | |
853 | &vc4_state->hw_dlist[vc4_state->ptr0_offset]); | |
854 | } | |
855 | ||
856 | static int vc4_plane_atomic_async_check(struct drm_plane *plane, | |
857 | struct drm_plane_state *state) | |
858 | { | |
859 | /* No configuring new scaling in the fast path. */ | |
860 | if (plane->state->crtc_w != state->crtc_w || | |
861 | plane->state->crtc_h != state->crtc_h || | |
862 | plane->state->src_w != state->src_w || | |
863 | plane->state->src_h != state->src_h) | |
864 | return -EINVAL; | |
865 | ||
866 | return 0; | |
867 | } | |
868 | ||
334dbd69 EA |
869 | static int vc4_prepare_fb(struct drm_plane *plane, |
870 | struct drm_plane_state *state) | |
871 | { | |
872 | struct vc4_bo *bo; | |
873 | struct dma_fence *fence; | |
b9f19259 | 874 | int ret; |
334dbd69 | 875 | |
2227a7a2 | 876 | if (!state->fb) |
334dbd69 EA |
877 | return 0; |
878 | ||
879 | bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); | |
b9f19259 | 880 | |
2227a7a2 SV |
881 | fence = reservation_object_get_excl_rcu(bo->resv); |
882 | drm_atomic_set_fence_for_plane(state, fence); | |
883 | ||
884 | if (plane->state->fb == state->fb) | |
885 | return 0; | |
886 | ||
b9f19259 BB |
887 | ret = vc4_bo_inc_usecnt(bo); |
888 | if (ret) | |
889 | return ret; | |
890 | ||
334dbd69 EA |
891 | return 0; |
892 | } | |
893 | ||
b9f19259 BB |
894 | static void vc4_cleanup_fb(struct drm_plane *plane, |
895 | struct drm_plane_state *state) | |
896 | { | |
897 | struct vc4_bo *bo; | |
898 | ||
899 | if (plane->state->fb == state->fb || !state->fb) | |
900 | return; | |
901 | ||
902 | bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); | |
903 | vc4_bo_dec_usecnt(bo); | |
904 | } | |
905 | ||
c8b75bca | 906 | static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { |
c8b75bca EA |
907 | .atomic_check = vc4_plane_atomic_check, |
908 | .atomic_update = vc4_plane_atomic_update, | |
334dbd69 | 909 | .prepare_fb = vc4_prepare_fb, |
b9f19259 | 910 | .cleanup_fb = vc4_cleanup_fb, |
539c320b GP |
911 | .atomic_async_check = vc4_plane_atomic_async_check, |
912 | .atomic_async_update = vc4_plane_atomic_async_update, | |
c8b75bca EA |
913 | }; |
914 | ||
915 | static void vc4_plane_destroy(struct drm_plane *plane) | |
916 | { | |
070473bc | 917 | drm_plane_helper_disable(plane, NULL); |
c8b75bca EA |
918 | drm_plane_cleanup(plane); |
919 | } | |
920 | ||
423ad7b3 DS |
921 | static bool vc4_format_mod_supported(struct drm_plane *plane, |
922 | uint32_t format, | |
923 | uint64_t modifier) | |
924 | { | |
925 | /* Support T_TILING for RGB formats only. */ | |
926 | switch (format) { | |
927 | case DRM_FORMAT_XRGB8888: | |
928 | case DRM_FORMAT_ARGB8888: | |
929 | case DRM_FORMAT_ABGR8888: | |
930 | case DRM_FORMAT_XBGR8888: | |
931 | case DRM_FORMAT_RGB565: | |
932 | case DRM_FORMAT_BGR565: | |
933 | case DRM_FORMAT_ARGB1555: | |
934 | case DRM_FORMAT_XRGB1555: | |
e065a8dd DS |
935 | switch (fourcc_mod_broadcom_mod(modifier)) { |
936 | case DRM_FORMAT_MOD_LINEAR: | |
937 | case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: | |
938 | case DRM_FORMAT_MOD_BROADCOM_SAND64: | |
939 | case DRM_FORMAT_MOD_BROADCOM_SAND128: | |
940 | return true; | |
941 | default: | |
942 | return false; | |
943 | } | |
944 | case DRM_FORMAT_NV12: | |
945 | case DRM_FORMAT_NV21: | |
946 | switch (fourcc_mod_broadcom_mod(modifier)) { | |
947 | case DRM_FORMAT_MOD_LINEAR: | |
948 | case DRM_FORMAT_MOD_BROADCOM_SAND64: | |
949 | case DRM_FORMAT_MOD_BROADCOM_SAND128: | |
950 | case DRM_FORMAT_MOD_BROADCOM_SAND256: | |
951 | return true; | |
952 | default: | |
953 | return false; | |
954 | } | |
423ad7b3 DS |
955 | case DRM_FORMAT_YUV422: |
956 | case DRM_FORMAT_YVU422: | |
957 | case DRM_FORMAT_YUV420: | |
958 | case DRM_FORMAT_YVU420: | |
423ad7b3 | 959 | case DRM_FORMAT_NV16: |
1e871d65 | 960 | case DRM_FORMAT_NV61: |
423ad7b3 DS |
961 | default: |
962 | return (modifier == DRM_FORMAT_MOD_LINEAR); | |
963 | } | |
964 | } | |
965 | ||
c8b75bca | 966 | static const struct drm_plane_funcs vc4_plane_funcs = { |
539c320b | 967 | .update_plane = drm_atomic_helper_update_plane, |
c8b75bca EA |
968 | .disable_plane = drm_atomic_helper_disable_plane, |
969 | .destroy = vc4_plane_destroy, | |
970 | .set_property = NULL, | |
971 | .reset = vc4_plane_reset, | |
972 | .atomic_duplicate_state = vc4_plane_duplicate_state, | |
973 | .atomic_destroy_state = vc4_plane_destroy_state, | |
423ad7b3 | 974 | .format_mod_supported = vc4_format_mod_supported, |
c8b75bca EA |
975 | }; |
976 | ||
977 | struct drm_plane *vc4_plane_init(struct drm_device *dev, | |
978 | enum drm_plane_type type) | |
979 | { | |
980 | struct drm_plane *plane = NULL; | |
981 | struct vc4_plane *vc4_plane; | |
982 | u32 formats[ARRAY_SIZE(hvs_formats)]; | |
fc04023f | 983 | u32 num_formats = 0; |
c8b75bca EA |
984 | int ret = 0; |
985 | unsigned i; | |
423ad7b3 DS |
986 | static const uint64_t modifiers[] = { |
987 | DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED, | |
e065a8dd DS |
988 | DRM_FORMAT_MOD_BROADCOM_SAND128, |
989 | DRM_FORMAT_MOD_BROADCOM_SAND64, | |
990 | DRM_FORMAT_MOD_BROADCOM_SAND256, | |
423ad7b3 DS |
991 | DRM_FORMAT_MOD_LINEAR, |
992 | DRM_FORMAT_MOD_INVALID | |
993 | }; | |
c8b75bca EA |
994 | |
995 | vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane), | |
996 | GFP_KERNEL); | |
7b347348 CIK |
997 | if (!vc4_plane) |
998 | return ERR_PTR(-ENOMEM); | |
c8b75bca | 999 | |
fc04023f EA |
1000 | for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { |
1001 | /* Don't allow YUV in cursor planes, since that means | |
1002 | * tuning on the scaler, which we don't allow for the | |
1003 | * cursor. | |
1004 | */ | |
1005 | if (type != DRM_PLANE_TYPE_CURSOR || | |
1006 | hvs_formats[i].hvs < HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE) { | |
1007 | formats[num_formats++] = hvs_formats[i].drm; | |
1008 | } | |
1009 | } | |
c8b75bca | 1010 | plane = &vc4_plane->base; |
49d29a07 | 1011 | ret = drm_universal_plane_init(dev, plane, 0, |
c8b75bca | 1012 | &vc4_plane_funcs, |
fc04023f | 1013 | formats, num_formats, |
423ad7b3 | 1014 | modifiers, type, NULL); |
c8b75bca EA |
1015 | |
1016 | drm_plane_helper_add(plane, &vc4_plane_helper_funcs); | |
1017 | ||
22445f03 SS |
1018 | drm_plane_create_alpha_property(plane); |
1019 | ||
c8b75bca | 1020 | return plane; |
c8b75bca | 1021 | } |