/* * Copyright © 2016 Broadcom * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "drmtest.h" #include "igt_aux.h" #include "igt_core.h" #include "igt_fb.h" #include "igt_vc4.h" #include "ioctl_wrappers.h" #include "intel_reg.h" #include "intel_chipset.h" #include "vc4_drm.h" #include "vc4_packet.h" #if NEW_CONTEXT_PARAM_NO_ERROR_CAPTURE_API #define LOCAL_CONTEXT_PARAM_NO_ERROR_CAPTURE 0x4 #endif /** * SECTION:igt_vc4 * @short_description: VC4 support library * @title: VC4 * @include: igt.h * * This library provides various auxiliary helper functions for writing VC4 * tests. */ /** * igt_vc4_get_cleared_bo: * @fd: device file descriptor * @size: size of the BO in bytes * @clearval: u32 value that the buffer should be completely cleared with * * This helper returns a new BO with the given size, which has just been * cleared using the render engine. */ uint32_t igt_vc4_get_cleared_bo(int fd, size_t size, uint32_t clearval) { /* A single row will be a page. */ uint32_t width = 1024; uint32_t height = size / (width * 4); uint32_t handle = igt_vc4_create_bo(fd, size); struct drm_vc4_submit_cl submit = { .color_write = { .hindex = 0, .bits = VC4_SET_FIELD(VC4_RENDER_CONFIG_FORMAT_RGBA8888, VC4_RENDER_CONFIG_FORMAT), }, .color_read = { .hindex = ~0 }, .zs_read = { .hindex = ~0 }, .zs_write = { .hindex = ~0 }, .msaa_color_write = { .hindex = ~0 }, .msaa_zs_write = { .hindex = ~0 }, .bo_handles = to_user_pointer(&handle), .bo_handle_count = 1, .width = width, .height = height, .max_x_tile = ALIGN(width, 64) / 64 - 1, .max_y_tile = ALIGN(height, 64) / 64 - 1, .clear_color = { clearval, clearval }, .flags = VC4_SUBMIT_CL_USE_CLEAR_COLOR, }; igt_assert_eq_u32(width * height * 4, size); do_ioctl(fd, DRM_IOCTL_VC4_SUBMIT_CL, &submit); return handle; } int igt_vc4_create_bo(int fd, size_t size) { struct drm_vc4_create_bo create = { .size = size, }; do_ioctl(fd, DRM_IOCTL_VC4_CREATE_BO, &create); return create.handle; } void * igt_vc4_mmap_bo(int fd, uint32_t handle, uint32_t size, unsigned prot) { struct drm_vc4_mmap_bo mmap_bo = { .handle = handle, }; void *ptr; do_ioctl(fd, DRM_IOCTL_VC4_MMAP_BO, &mmap_bo); ptr = mmap(0, size, prot, MAP_SHARED, fd, mmap_bo.offset); if (ptr == MAP_FAILED) return NULL; else return ptr; } void igt_vc4_set_tiling(int fd, uint32_t handle, uint64_t modifier) { struct drm_vc4_set_tiling set = { .handle = handle, .modifier = modifier, }; do_ioctl(fd, DRM_IOCTL_VC4_SET_TILING, &set); } uint64_t igt_vc4_get_tiling(int fd, uint32_t handle) { struct drm_vc4_get_tiling get = { .handle = handle, }; do_ioctl(fd, DRM_IOCTL_VC4_GET_TILING, &get); return get.modifier; } int igt_vc4_get_param(int fd, uint32_t param, uint64_t *val) { struct drm_vc4_get_param arg = { .param = param, }; int ret; ret = igt_ioctl(fd, DRM_IOCTL_VC4_GET_PARAM, &arg); if (ret) return ret; *val = arg.value; return 0; } bool igt_vc4_purgeable_bo(int fd, int handle, bool purgeable) { struct drm_vc4_gem_madvise arg = { .handle = handle, .madv = purgeable ? VC4_MADV_DONTNEED : VC4_MADV_WILLNEED, }; do_ioctl(fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg); return arg.retained; } unsigned int igt_vc4_fb_t_tiled_convert(struct igt_fb *dst, struct igt_fb *src) { unsigned int fb_id; unsigned int i, j; void *src_buf; void *dst_buf; size_t bpp = src->plane_bpp[0]; size_t dst_stride = ALIGN(src->strides[0], 128); fb_id = igt_create_fb_with_bo_size(src->fd, src->width, src->height, src->drm_format, DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED, dst, 0, dst_stride); igt_assert(fb_id > 0); igt_assert(bpp == 16 || bpp == 32); src_buf = igt_fb_map_buffer(src->fd, src); igt_assert(src_buf); dst_buf = igt_fb_map_buffer(dst->fd, dst); igt_assert(dst_buf); for (i = 0; i < src->height; i++) { for (j = 0; j < src->width; j++) { size_t src_offset = src->offsets[0]; size_t dst_offset = dst->offsets[0]; src_offset += src->strides[0] * i + j * bpp / 8; dst_offset += igt_vc4_t_tiled_offset(dst_stride, src->height, bpp, j, i); switch (bpp) { case 16: *(uint16_t *)(dst_buf + dst_offset) = *(uint16_t *)(src_buf + src_offset); break; case 32: *(uint32_t *)(dst_buf + dst_offset) = *(uint32_t *)(src_buf + src_offset); break; } } } igt_fb_unmap_buffer(src, src_buf); igt_fb_unmap_buffer(dst, dst_buf); return fb_id; } /* Calculate the t-tile width so that size = width * height * bpp / 8. */ #define VC4_T_TILE_W(size, height, bpp) ((size) / (height) / ((bpp) / 8)) size_t igt_vc4_t_tiled_offset(size_t stride, size_t height, size_t bpp, size_t x, size_t y) { const size_t t1k_map_even[] = { 0, 3, 1, 2 }; const size_t t1k_map_odd[] = { 2, 1, 3, 0 }; const size_t t4k_t_h = 32; const size_t t1k_t_h = 16; const size_t t64_t_h = 4; size_t offset = 0; size_t t4k_t_w, t4k_w, t4k_x, t4k_y; size_t t1k_t_w, t1k_x, t1k_y; size_t t64_t_w, t64_x, t64_y; size_t pix_x, pix_y; unsigned int index; /* T-tiling is only supported for 16 and 32 bpp. */ igt_assert(bpp == 16 || bpp == 32); /* T-tiling stride must be aligned to the 4K tiles strides. */ igt_assert((stride % (4096 / t4k_t_h)) == 0); /* Calculate the tile width for the bpp. */ t4k_t_w = VC4_T_TILE_W(4096, t4k_t_h, bpp); t1k_t_w = VC4_T_TILE_W(1024, t1k_t_h, bpp); t64_t_w = VC4_T_TILE_W(64, t64_t_h, bpp); /* Aligned total width in number of 4K tiles. */ t4k_w = (stride / (bpp / 8)) / t4k_t_w; /* X and y coordinates in number of 4K tiles. */ t4k_x = x / t4k_t_w; t4k_y = y / t4k_t_h; /* Increase offset to the beginning of the 4K tile row. */ offset += t4k_y * t4k_w * 4096; /* X and Y coordinates in number of 1K tiles within the 4K tile. */ t1k_x = (x % t4k_t_w) / t1k_t_w; t1k_y = (y % t4k_t_h) / t1k_t_h; /* Index for 1K tile map lookup. */ index = 2 * t1k_y + t1k_x; /* Odd rows start from the right, even rows from the left. */ if (t4k_y % 2) { /* Increase offset to the 4K tile (starting from the right). */ offset += (t4k_w - t4k_x - 1) * 4096; /* Incrase offset to the beginning of the (odd) 1K tile. */ offset += t1k_map_odd[index] * 1024; } else { /* Increase offset to the 4K tile (starting from the left). */ offset += t4k_x * 4096; /* Incrase offset to the beginning of the (even) 1K tile. */ offset += t1k_map_even[index] * 1024; } /* X and Y coordinates in number of 64 byte tiles within the 1K tile. */ t64_x = (x % t1k_t_w) / t64_t_w; t64_y = (y % t1k_t_h) / t64_t_h; /* Increase offset to the beginning of the 64-byte tile. */ offset += (t64_y * (t1k_t_w / t64_t_w) + t64_x) * 64; /* X and Y coordinates in number of pixels within the 64-byte tile. */ pix_x = x % t64_t_w; pix_y = y % t64_t_h; /* Increase offset to the correct pixel. */ offset += (pix_y * t64_t_w + pix_x) * bpp / 8; return offset; } static void vc4_fb_sand_tiled_convert_plane(struct igt_fb *dst, void *dst_buf, struct igt_fb *src, void *src_buf, size_t column_width_bytes, size_t column_height, unsigned int plane) { size_t bpp = dst->plane_bpp[plane]; size_t column_width = column_width_bytes * dst->plane_width[plane] / dst->width; size_t column_size = column_width_bytes * column_height; unsigned int i, j; for (i = 0; i < dst->plane_height[plane]; i++) { for (j = 0; j < src->plane_width[plane]; j++) { size_t src_offset = src->offsets[plane]; size_t dst_offset = dst->offsets[plane]; src_offset += src->strides[plane] * i + j * bpp / 8; dst_offset += vc4_sand_tiled_offset(column_width, column_size, j, i, bpp); switch (bpp) { case 8: *(uint8_t *)(dst_buf + dst_offset) = *(uint8_t *)(src_buf + src_offset); break; case 16: *(uint16_t *)(dst_buf + dst_offset) = *(uint16_t *)(src_buf + src_offset); break; default: igt_assert(false); } } } } unsigned int vc4_fb_sand_tiled_convert(struct igt_fb *dst, struct igt_fb *src, uint64_t modifier) { uint64_t modifier_base; size_t column_width_bytes; size_t column_height; unsigned int fb_id; unsigned int i; void *src_buf; void *dst_buf; modifier_base = fourcc_mod_broadcom_mod(modifier); column_height = fourcc_mod_broadcom_param(modifier); switch (modifier_base) { case DRM_FORMAT_MOD_BROADCOM_SAND32: column_width_bytes = 32; break; case DRM_FORMAT_MOD_BROADCOM_SAND64: column_width_bytes = 64; break; case DRM_FORMAT_MOD_BROADCOM_SAND128: column_width_bytes = 128; break; case DRM_FORMAT_MOD_BROADCOM_SAND256: column_width_bytes = 256; break; default: igt_assert(false); } fb_id = igt_create_fb(src->fd, src->width, src->height, src->drm_format, modifier, dst); igt_assert(fb_id > 0); src_buf = igt_fb_map_buffer(src->fd, src); igt_assert(src_buf); dst_buf = igt_fb_map_buffer(dst->fd, dst); igt_assert(dst_buf); for (i = 0; i < dst->num_planes; i++) vc4_fb_sand_tiled_convert_plane(dst, dst_buf, src, src_buf, column_width_bytes, column_height, i); igt_fb_unmap_buffer(src, src_buf); igt_fb_unmap_buffer(dst, dst_buf); return fb_id; } size_t vc4_sand_tiled_offset(size_t column_width, size_t column_size, size_t x, size_t y, size_t bpp) { size_t offset = 0; size_t cols_x; size_t pix_x; /* Offset to the beginning of the relevant column. */ cols_x = x / column_width; offset += cols_x * column_size; /* Offset to the relevant pixel. */ pix_x = x % column_width; offset += (column_width * y + pix_x) * bpp / 8; return offset; }