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