1 | /* |
2 | * Copyright © 2014-2015 Broadcom |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice (including the next |
12 | * paragraph) shall be included in all copies or substantial portions of the |
13 | * Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
21 | * IN THE SOFTWARE. |
22 | */ |
23 | |
24 | /** |
25 | * DOC: Render command list generation |
26 | * |
27 | * In the V3D hardware, render command lists are what load and store |
28 | * tiles of a framebuffer and optionally call out to binner-generated |
29 | * command lists to do the 3D drawing for that tile. |
30 | * |
31 | * In the VC4 driver, render command list generation is performed by the |
32 | * kernel instead of userspace. We do this because validating a |
33 | * user-submitted command list is hard to get right and has high CPU overhead, |
34 | * while the number of valid configurations for render command lists is |
35 | * actually fairly low. |
36 | */ |
37 | |
38 | #include "uapi/drm/vc4_drm.h" |
39 | #include "vc4_drv.h" |
40 | #include "vc4_packet.h" |
41 | |
42 | struct vc4_rcl_setup { |
43 | struct drm_gem_dma_object *color_read; |
44 | struct drm_gem_dma_object *color_write; |
45 | struct drm_gem_dma_object *zs_read; |
46 | struct drm_gem_dma_object *zs_write; |
47 | struct drm_gem_dma_object *msaa_color_write; |
48 | struct drm_gem_dma_object *msaa_zs_write; |
49 | |
50 | struct drm_gem_dma_object *rcl; |
51 | u32 next_offset; |
52 | |
53 | u32 next_write_bo_index; |
54 | }; |
55 | |
56 | static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val) |
57 | { |
58 | *(u8 *)(setup->rcl->vaddr + setup->next_offset) = val; |
59 | setup->next_offset += 1; |
60 | } |
61 | |
62 | static inline void rcl_u16(struct vc4_rcl_setup *setup, u16 val) |
63 | { |
64 | *(u16 *)(setup->rcl->vaddr + setup->next_offset) = val; |
65 | setup->next_offset += 2; |
66 | } |
67 | |
68 | static inline void rcl_u32(struct vc4_rcl_setup *setup, u32 val) |
69 | { |
70 | *(u32 *)(setup->rcl->vaddr + setup->next_offset) = val; |
71 | setup->next_offset += 4; |
72 | } |
73 | |
74 | /* |
75 | * Emits a no-op STORE_TILE_BUFFER_GENERAL. |
76 | * |
77 | * If we emit a PACKET_TILE_COORDINATES, it must be followed by a store of |
78 | * some sort before another load is triggered. |
79 | */ |
80 | static void vc4_store_before_load(struct vc4_rcl_setup *setup) |
81 | { |
82 | rcl_u8(setup, val: VC4_PACKET_STORE_TILE_BUFFER_GENERAL); |
83 | rcl_u16(setup, |
84 | VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_NONE, |
85 | VC4_LOADSTORE_TILE_BUFFER_BUFFER) | |
86 | VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR | |
87 | VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR | |
88 | VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR); |
89 | rcl_u32(setup, val: 0); /* no address, since we're in None mode */ |
90 | } |
91 | |
92 | /* |
93 | * Calculates the physical address of the start of a tile in a RCL surface. |
94 | * |
95 | * Unlike the other load/store packets, |
96 | * VC4_PACKET_LOAD/STORE_FULL_RES_TILE_BUFFER don't look at the tile |
97 | * coordinates packet, and instead just store to the address given. |
98 | */ |
99 | static uint32_t vc4_full_res_offset(struct vc4_exec_info *exec, |
100 | struct drm_gem_dma_object *bo, |
101 | struct drm_vc4_submit_rcl_surface *surf, |
102 | uint8_t x, uint8_t y) |
103 | { |
104 | return bo->dma_addr + surf->offset + VC4_TILE_BUFFER_SIZE * |
105 | (DIV_ROUND_UP(exec->args->width, 32) * y + x); |
106 | } |
107 | |
108 | /* |
109 | * Emits a PACKET_TILE_COORDINATES if one isn't already pending. |
110 | * |
111 | * The tile coordinates packet triggers a pending load if there is one, are |
112 | * used for clipping during rendering, and determine where loads/stores happen |
113 | * relative to their base address. |
114 | */ |
115 | static void vc4_tile_coordinates(struct vc4_rcl_setup *setup, |
116 | uint32_t x, uint32_t y) |
117 | { |
118 | rcl_u8(setup, val: VC4_PACKET_TILE_COORDINATES); |
119 | rcl_u8(setup, val: x); |
120 | rcl_u8(setup, val: y); |
121 | } |
122 | |
123 | static void emit_tile(struct vc4_exec_info *exec, |
124 | struct vc4_rcl_setup *setup, |
125 | uint8_t x, uint8_t y, bool first, bool last) |
126 | { |
127 | struct drm_vc4_submit_cl *args = exec->args; |
128 | bool has_bin = args->bin_cl_size != 0; |
129 | |
130 | /* Note that the load doesn't actually occur until the |
131 | * tile coords packet is processed, and only one load |
132 | * may be outstanding at a time. |
133 | */ |
134 | if (setup->color_read) { |
135 | if (args->color_read.flags & |
136 | VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { |
137 | rcl_u8(setup, val: VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); |
138 | rcl_u32(setup, |
139 | val: vc4_full_res_offset(exec, bo: setup->color_read, |
140 | surf: &args->color_read, x, y) | |
141 | VC4_LOADSTORE_FULL_RES_DISABLE_ZS); |
142 | } else { |
143 | rcl_u8(setup, val: VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); |
144 | rcl_u16(setup, val: args->color_read.bits); |
145 | rcl_u32(setup, val: setup->color_read->dma_addr + |
146 | args->color_read.offset); |
147 | } |
148 | } |
149 | |
150 | if (setup->zs_read) { |
151 | if (setup->color_read) { |
152 | /* Exec previous load. */ |
153 | vc4_tile_coordinates(setup, x, y); |
154 | vc4_store_before_load(setup); |
155 | } |
156 | |
157 | if (args->zs_read.flags & |
158 | VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { |
159 | rcl_u8(setup, val: VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); |
160 | rcl_u32(setup, |
161 | val: vc4_full_res_offset(exec, bo: setup->zs_read, |
162 | surf: &args->zs_read, x, y) | |
163 | VC4_LOADSTORE_FULL_RES_DISABLE_COLOR); |
164 | } else { |
165 | rcl_u8(setup, val: VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); |
166 | rcl_u16(setup, val: args->zs_read.bits); |
167 | rcl_u32(setup, val: setup->zs_read->dma_addr + |
168 | args->zs_read.offset); |
169 | } |
170 | } |
171 | |
172 | /* Clipping depends on tile coordinates having been |
173 | * emitted, so we always need one here. |
174 | */ |
175 | vc4_tile_coordinates(setup, x, y); |
176 | |
177 | /* Wait for the binner before jumping to the first |
178 | * tile's lists. |
179 | */ |
180 | if (first && has_bin) |
181 | rcl_u8(setup, val: VC4_PACKET_WAIT_ON_SEMAPHORE); |
182 | |
183 | if (has_bin) { |
184 | rcl_u8(setup, val: VC4_PACKET_BRANCH_TO_SUB_LIST); |
185 | rcl_u32(setup, val: (exec->tile_alloc_offset + |
186 | (y * exec->bin_tiles_x + x) * 32)); |
187 | } |
188 | |
189 | if (setup->msaa_color_write) { |
190 | bool last_tile_write = (!setup->msaa_zs_write && |
191 | !setup->zs_write && |
192 | !setup->color_write); |
193 | uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_ZS; |
194 | |
195 | if (!last_tile_write) |
196 | bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; |
197 | else if (last) |
198 | bits |= VC4_LOADSTORE_FULL_RES_EOF; |
199 | rcl_u8(setup, val: VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); |
200 | rcl_u32(setup, |
201 | val: vc4_full_res_offset(exec, bo: setup->msaa_color_write, |
202 | surf: &args->msaa_color_write, x, y) | |
203 | bits); |
204 | } |
205 | |
206 | if (setup->msaa_zs_write) { |
207 | bool last_tile_write = (!setup->zs_write && |
208 | !setup->color_write); |
209 | uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_COLOR; |
210 | |
211 | if (setup->msaa_color_write) |
212 | vc4_tile_coordinates(setup, x, y); |
213 | if (!last_tile_write) |
214 | bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; |
215 | else if (last) |
216 | bits |= VC4_LOADSTORE_FULL_RES_EOF; |
217 | rcl_u8(setup, val: VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); |
218 | rcl_u32(setup, |
219 | val: vc4_full_res_offset(exec, bo: setup->msaa_zs_write, |
220 | surf: &args->msaa_zs_write, x, y) | |
221 | bits); |
222 | } |
223 | |
224 | if (setup->zs_write) { |
225 | bool last_tile_write = !setup->color_write; |
226 | |
227 | if (setup->msaa_color_write || setup->msaa_zs_write) |
228 | vc4_tile_coordinates(setup, x, y); |
229 | |
230 | rcl_u8(setup, val: VC4_PACKET_STORE_TILE_BUFFER_GENERAL); |
231 | rcl_u16(setup, val: args->zs_write.bits | |
232 | (last_tile_write ? |
233 | 0 : VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR)); |
234 | rcl_u32(setup, |
235 | val: (setup->zs_write->dma_addr + args->zs_write.offset) | |
236 | ((last && last_tile_write) ? |
237 | VC4_LOADSTORE_TILE_BUFFER_EOF : 0)); |
238 | } |
239 | |
240 | if (setup->color_write) { |
241 | if (setup->msaa_color_write || setup->msaa_zs_write || |
242 | setup->zs_write) { |
243 | vc4_tile_coordinates(setup, x, y); |
244 | } |
245 | |
246 | if (last) |
247 | rcl_u8(setup, val: VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF); |
248 | else |
249 | rcl_u8(setup, val: VC4_PACKET_STORE_MS_TILE_BUFFER); |
250 | } |
251 | } |
252 | |
253 | static int vc4_create_rcl_bo(struct drm_device *dev, struct vc4_exec_info *exec, |
254 | struct vc4_rcl_setup *setup) |
255 | { |
256 | struct drm_vc4_submit_cl *args = exec->args; |
257 | bool has_bin = args->bin_cl_size != 0; |
258 | uint8_t min_x_tile = args->min_x_tile; |
259 | uint8_t min_y_tile = args->min_y_tile; |
260 | uint8_t max_x_tile = args->max_x_tile; |
261 | uint8_t max_y_tile = args->max_y_tile; |
262 | uint8_t xtiles = max_x_tile - min_x_tile + 1; |
263 | uint8_t ytiles = max_y_tile - min_y_tile + 1; |
264 | uint8_t xi, yi; |
265 | uint32_t size, loop_body_size; |
266 | bool positive_x = true; |
267 | bool positive_y = true; |
268 | |
269 | if (args->flags & VC4_SUBMIT_CL_FIXED_RCL_ORDER) { |
270 | if (!(args->flags & VC4_SUBMIT_CL_RCL_ORDER_INCREASING_X)) |
271 | positive_x = false; |
272 | if (!(args->flags & VC4_SUBMIT_CL_RCL_ORDER_INCREASING_Y)) |
273 | positive_y = false; |
274 | } |
275 | |
276 | size = VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE; |
277 | loop_body_size = VC4_PACKET_TILE_COORDINATES_SIZE; |
278 | |
279 | if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { |
280 | size += VC4_PACKET_CLEAR_COLORS_SIZE + |
281 | VC4_PACKET_TILE_COORDINATES_SIZE + |
282 | VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; |
283 | } |
284 | |
285 | if (setup->color_read) { |
286 | if (args->color_read.flags & |
287 | VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { |
288 | loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; |
289 | } else { |
290 | loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; |
291 | } |
292 | } |
293 | if (setup->zs_read) { |
294 | if (setup->color_read) { |
295 | loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; |
296 | loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; |
297 | } |
298 | |
299 | if (args->zs_read.flags & |
300 | VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { |
301 | loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; |
302 | } else { |
303 | loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; |
304 | } |
305 | } |
306 | |
307 | if (has_bin) { |
308 | size += VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE; |
309 | loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE; |
310 | } |
311 | |
312 | if (setup->msaa_color_write) |
313 | loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; |
314 | if (setup->msaa_zs_write) |
315 | loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; |
316 | |
317 | if (setup->zs_write) |
318 | loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; |
319 | if (setup->color_write) |
320 | loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE; |
321 | |
322 | /* We need a VC4_PACKET_TILE_COORDINATES in between each store. */ |
323 | loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE * |
324 | ((setup->msaa_color_write != NULL) + |
325 | (setup->msaa_zs_write != NULL) + |
326 | (setup->color_write != NULL) + |
327 | (setup->zs_write != NULL) - 1); |
328 | |
329 | size += xtiles * ytiles * loop_body_size; |
330 | |
331 | setup->rcl = &vc4_bo_create(dev, size, from_cache: true, type: VC4_BO_TYPE_RCL)->base; |
332 | if (IS_ERR(ptr: setup->rcl)) |
333 | return PTR_ERR(ptr: setup->rcl); |
334 | list_add_tail(new: &to_vc4_bo(&setup->rcl->base)->unref_head, |
335 | head: &exec->unref_list); |
336 | |
337 | /* The tile buffer gets cleared when the previous tile is stored. If |
338 | * the clear values changed between frames, then the tile buffer has |
339 | * stale clear values in it, so we have to do a store in None mode (no |
340 | * writes) so that we trigger the tile buffer clear. |
341 | */ |
342 | if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { |
343 | rcl_u8(setup, val: VC4_PACKET_CLEAR_COLORS); |
344 | rcl_u32(setup, val: args->clear_color[0]); |
345 | rcl_u32(setup, val: args->clear_color[1]); |
346 | rcl_u32(setup, val: args->clear_z); |
347 | rcl_u8(setup, val: args->clear_s); |
348 | |
349 | vc4_tile_coordinates(setup, x: 0, y: 0); |
350 | |
351 | rcl_u8(setup, val: VC4_PACKET_STORE_TILE_BUFFER_GENERAL); |
352 | rcl_u16(setup, VC4_LOADSTORE_TILE_BUFFER_NONE); |
353 | rcl_u32(setup, val: 0); /* no address, since we're in None mode */ |
354 | } |
355 | |
356 | rcl_u8(setup, val: VC4_PACKET_TILE_RENDERING_MODE_CONFIG); |
357 | rcl_u32(setup, |
358 | val: (setup->color_write ? (setup->color_write->dma_addr + |
359 | args->color_write.offset) : |
360 | 0)); |
361 | rcl_u16(setup, val: args->width); |
362 | rcl_u16(setup, val: args->height); |
363 | rcl_u16(setup, val: args->color_write.bits); |
364 | |
365 | for (yi = 0; yi < ytiles; yi++) { |
366 | int y = positive_y ? min_y_tile + yi : max_y_tile - yi; |
367 | for (xi = 0; xi < xtiles; xi++) { |
368 | int x = positive_x ? min_x_tile + xi : max_x_tile - xi; |
369 | bool first = (xi == 0 && yi == 0); |
370 | bool last = (xi == xtiles - 1 && yi == ytiles - 1); |
371 | |
372 | emit_tile(exec, setup, x, y, first, last); |
373 | } |
374 | } |
375 | |
376 | BUG_ON(setup->next_offset != size); |
377 | exec->ct1ca = setup->rcl->dma_addr; |
378 | exec->ct1ea = setup->rcl->dma_addr + setup->next_offset; |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | static int vc4_full_res_bounds_check(struct vc4_exec_info *exec, |
384 | struct drm_gem_dma_object *obj, |
385 | struct drm_vc4_submit_rcl_surface *surf) |
386 | { |
387 | struct drm_vc4_submit_cl *args = exec->args; |
388 | u32 render_tiles_stride = DIV_ROUND_UP(exec->args->width, 32); |
389 | |
390 | if (surf->offset > obj->base.size) { |
391 | DRM_DEBUG("surface offset %d > BO size %zd\n" , |
392 | surf->offset, obj->base.size); |
393 | return -EINVAL; |
394 | } |
395 | |
396 | if ((obj->base.size - surf->offset) / VC4_TILE_BUFFER_SIZE < |
397 | render_tiles_stride * args->max_y_tile + args->max_x_tile) { |
398 | DRM_DEBUG("MSAA tile %d, %d out of bounds " |
399 | "(bo size %zd, offset %d).\n" , |
400 | args->max_x_tile, args->max_y_tile, |
401 | obj->base.size, |
402 | surf->offset); |
403 | return -EINVAL; |
404 | } |
405 | |
406 | return 0; |
407 | } |
408 | |
409 | static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec, |
410 | struct drm_gem_dma_object **obj, |
411 | struct drm_vc4_submit_rcl_surface *surf) |
412 | { |
413 | if (surf->flags != 0 || surf->bits != 0) { |
414 | DRM_DEBUG("MSAA surface had nonzero flags/bits\n" ); |
415 | return -EINVAL; |
416 | } |
417 | |
418 | if (surf->hindex == ~0) |
419 | return 0; |
420 | |
421 | *obj = vc4_use_bo(exec, hindex: surf->hindex); |
422 | if (!*obj) |
423 | return -EINVAL; |
424 | |
425 | exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj; |
426 | |
427 | if (surf->offset & 0xf) { |
428 | DRM_DEBUG("MSAA write must be 16b aligned.\n" ); |
429 | return -EINVAL; |
430 | } |
431 | |
432 | return vc4_full_res_bounds_check(exec, obj: *obj, surf); |
433 | } |
434 | |
435 | static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, |
436 | struct drm_gem_dma_object **obj, |
437 | struct drm_vc4_submit_rcl_surface *surf, |
438 | bool is_write) |
439 | { |
440 | uint8_t tiling = VC4_GET_FIELD(surf->bits, |
441 | VC4_LOADSTORE_TILE_BUFFER_TILING); |
442 | uint8_t buffer = VC4_GET_FIELD(surf->bits, |
443 | VC4_LOADSTORE_TILE_BUFFER_BUFFER); |
444 | uint8_t format = VC4_GET_FIELD(surf->bits, |
445 | VC4_LOADSTORE_TILE_BUFFER_FORMAT); |
446 | int cpp; |
447 | int ret; |
448 | |
449 | if (surf->flags & ~VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { |
450 | DRM_DEBUG("Extra flags set\n" ); |
451 | return -EINVAL; |
452 | } |
453 | |
454 | if (surf->hindex == ~0) |
455 | return 0; |
456 | |
457 | *obj = vc4_use_bo(exec, hindex: surf->hindex); |
458 | if (!*obj) |
459 | return -EINVAL; |
460 | |
461 | if (is_write) |
462 | exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj; |
463 | |
464 | if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { |
465 | if (surf == &exec->args->zs_write) { |
466 | DRM_DEBUG("general zs write may not be a full-res.\n" ); |
467 | return -EINVAL; |
468 | } |
469 | |
470 | if (surf->bits != 0) { |
471 | DRM_DEBUG("load/store general bits set with " |
472 | "full res load/store.\n" ); |
473 | return -EINVAL; |
474 | } |
475 | |
476 | ret = vc4_full_res_bounds_check(exec, obj: *obj, surf); |
477 | if (ret) |
478 | return ret; |
479 | |
480 | return 0; |
481 | } |
482 | |
483 | if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK | |
484 | VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK | |
485 | VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) { |
486 | DRM_DEBUG("Unknown bits in load/store: 0x%04x\n" , |
487 | surf->bits); |
488 | return -EINVAL; |
489 | } |
490 | |
491 | if (tiling > VC4_TILING_FORMAT_LT) { |
492 | DRM_DEBUG("Bad tiling format\n" ); |
493 | return -EINVAL; |
494 | } |
495 | |
496 | if (buffer == VC4_LOADSTORE_TILE_BUFFER_ZS) { |
497 | if (format != 0) { |
498 | DRM_DEBUG("No color format should be set for ZS\n" ); |
499 | return -EINVAL; |
500 | } |
501 | cpp = 4; |
502 | } else if (buffer == VC4_LOADSTORE_TILE_BUFFER_COLOR) { |
503 | switch (format) { |
504 | case VC4_LOADSTORE_TILE_BUFFER_BGR565: |
505 | case VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER: |
506 | cpp = 2; |
507 | break; |
508 | case VC4_LOADSTORE_TILE_BUFFER_RGBA8888: |
509 | cpp = 4; |
510 | break; |
511 | default: |
512 | DRM_DEBUG("Bad tile buffer format\n" ); |
513 | return -EINVAL; |
514 | } |
515 | } else { |
516 | DRM_DEBUG("Bad load/store buffer %d.\n" , buffer); |
517 | return -EINVAL; |
518 | } |
519 | |
520 | if (surf->offset & 0xf) { |
521 | DRM_DEBUG("load/store buffer must be 16b aligned.\n" ); |
522 | return -EINVAL; |
523 | } |
524 | |
525 | if (!vc4_check_tex_size(exec, fbo: *obj, offset: surf->offset, tiling_format: tiling, |
526 | width: exec->args->width, height: exec->args->height, cpp)) { |
527 | return -EINVAL; |
528 | } |
529 | |
530 | return 0; |
531 | } |
532 | |
533 | static int |
534 | vc4_rcl_render_config_surface_setup(struct vc4_exec_info *exec, |
535 | struct vc4_rcl_setup *setup, |
536 | struct drm_gem_dma_object **obj, |
537 | struct drm_vc4_submit_rcl_surface *surf) |
538 | { |
539 | uint8_t tiling = VC4_GET_FIELD(surf->bits, |
540 | VC4_RENDER_CONFIG_MEMORY_FORMAT); |
541 | uint8_t format = VC4_GET_FIELD(surf->bits, |
542 | VC4_RENDER_CONFIG_FORMAT); |
543 | int cpp; |
544 | |
545 | if (surf->flags != 0) { |
546 | DRM_DEBUG("No flags supported on render config.\n" ); |
547 | return -EINVAL; |
548 | } |
549 | |
550 | if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK | |
551 | VC4_RENDER_CONFIG_FORMAT_MASK | |
552 | VC4_RENDER_CONFIG_MS_MODE_4X | |
553 | VC4_RENDER_CONFIG_DECIMATE_MODE_4X)) { |
554 | DRM_DEBUG("Unknown bits in render config: 0x%04x\n" , |
555 | surf->bits); |
556 | return -EINVAL; |
557 | } |
558 | |
559 | if (surf->hindex == ~0) |
560 | return 0; |
561 | |
562 | *obj = vc4_use_bo(exec, hindex: surf->hindex); |
563 | if (!*obj) |
564 | return -EINVAL; |
565 | |
566 | exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj; |
567 | |
568 | if (tiling > VC4_TILING_FORMAT_LT) { |
569 | DRM_DEBUG("Bad tiling format\n" ); |
570 | return -EINVAL; |
571 | } |
572 | |
573 | switch (format) { |
574 | case VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED: |
575 | case VC4_RENDER_CONFIG_FORMAT_BGR565: |
576 | cpp = 2; |
577 | break; |
578 | case VC4_RENDER_CONFIG_FORMAT_RGBA8888: |
579 | cpp = 4; |
580 | break; |
581 | default: |
582 | DRM_DEBUG("Bad tile buffer format\n" ); |
583 | return -EINVAL; |
584 | } |
585 | |
586 | if (!vc4_check_tex_size(exec, fbo: *obj, offset: surf->offset, tiling_format: tiling, |
587 | width: exec->args->width, height: exec->args->height, cpp)) { |
588 | return -EINVAL; |
589 | } |
590 | |
591 | return 0; |
592 | } |
593 | |
594 | int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec) |
595 | { |
596 | struct vc4_dev *vc4 = to_vc4_dev(dev); |
597 | struct vc4_rcl_setup setup = {0}; |
598 | struct drm_vc4_submit_cl *args = exec->args; |
599 | bool has_bin = args->bin_cl_size != 0; |
600 | int ret; |
601 | |
602 | if (WARN_ON_ONCE(vc4->is_vc5)) |
603 | return -ENODEV; |
604 | |
605 | if (args->min_x_tile > args->max_x_tile || |
606 | args->min_y_tile > args->max_y_tile) { |
607 | DRM_DEBUG("Bad render tile set (%d,%d)-(%d,%d)\n" , |
608 | args->min_x_tile, args->min_y_tile, |
609 | args->max_x_tile, args->max_y_tile); |
610 | return -EINVAL; |
611 | } |
612 | |
613 | if (has_bin && |
614 | (args->max_x_tile > exec->bin_tiles_x || |
615 | args->max_y_tile > exec->bin_tiles_y)) { |
616 | DRM_DEBUG("Render tiles (%d,%d) outside of bin config " |
617 | "(%d,%d)\n" , |
618 | args->max_x_tile, args->max_y_tile, |
619 | exec->bin_tiles_x, exec->bin_tiles_y); |
620 | return -EINVAL; |
621 | } |
622 | |
623 | ret = vc4_rcl_render_config_surface_setup(exec, setup: &setup, |
624 | obj: &setup.color_write, |
625 | surf: &args->color_write); |
626 | if (ret) |
627 | return ret; |
628 | |
629 | ret = vc4_rcl_surface_setup(exec, obj: &setup.color_read, surf: &args->color_read, |
630 | is_write: false); |
631 | if (ret) |
632 | return ret; |
633 | |
634 | ret = vc4_rcl_surface_setup(exec, obj: &setup.zs_read, surf: &args->zs_read, |
635 | is_write: false); |
636 | if (ret) |
637 | return ret; |
638 | |
639 | ret = vc4_rcl_surface_setup(exec, obj: &setup.zs_write, surf: &args->zs_write, |
640 | is_write: true); |
641 | if (ret) |
642 | return ret; |
643 | |
644 | ret = vc4_rcl_msaa_surface_setup(exec, obj: &setup.msaa_color_write, |
645 | surf: &args->msaa_color_write); |
646 | if (ret) |
647 | return ret; |
648 | |
649 | ret = vc4_rcl_msaa_surface_setup(exec, obj: &setup.msaa_zs_write, |
650 | surf: &args->msaa_zs_write); |
651 | if (ret) |
652 | return ret; |
653 | |
654 | /* We shouldn't even have the job submitted to us if there's no |
655 | * surface to write out. |
656 | */ |
657 | if (!setup.color_write && !setup.zs_write && |
658 | !setup.msaa_color_write && !setup.msaa_zs_write) { |
659 | DRM_DEBUG("RCL requires color or Z/S write\n" ); |
660 | return -EINVAL; |
661 | } |
662 | |
663 | return vc4_create_rcl_bo(dev, exec, setup: &setup); |
664 | } |
665 | |