blob: 4415fa321ceb856aa818532aa32bc55854ba0af0 [file] [log] [blame]
/*
* 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 <assert.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#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.
*/
bool igt_vc4_is_tiled(uint64_t modifier)
{
if (modifier >> 56ULL != DRM_FORMAT_MOD_VENDOR_BROADCOM)
return false;
switch (fourcc_mod_broadcom_mod(modifier)) {
case DRM_FORMAT_MOD_BROADCOM_SAND32:
case DRM_FORMAT_MOD_BROADCOM_SAND64:
case DRM_FORMAT_MOD_BROADCOM_SAND128:
case DRM_FORMAT_MOD_BROADCOM_SAND256:
case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
return true;
default:
return false;
}
}
/**
* 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;
}
/* Calculate the t-tile width so that size = width * height * bpp / 8. */
#define VC4_T_TILE_W(size, height, bpp) ((size) / (height) / ((bpp) / 8))
static 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_convert_plane_to_t_tiled(struct igt_fb *dst, void *dst_buf,
struct igt_fb *src, void *src_buf,
unsigned int plane)
{
size_t bpp = src->plane_bpp[plane];
unsigned int i, j;
for (i = 0; i < src->height; i++) {
for (j = 0; j < src->width; 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 += igt_vc4_t_tiled_offset(dst->strides[plane],
dst->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;
}
}
}
}
static void vc4_fb_convert_plane_from_t_tiled(struct igt_fb *dst, void *dst_buf,
struct igt_fb *src, void *src_buf,
unsigned int plane)
{
size_t bpp = src->plane_bpp[plane];
unsigned int i, j;
for (i = 0; i < src->height; i++) {
for (j = 0; j < src->width; j++) {
size_t src_offset = src->offsets[plane];
size_t dst_offset = dst->offsets[plane];
src_offset += igt_vc4_t_tiled_offset(src->strides[plane],
src->height,
bpp, j, i);
src_offset += dst->strides[plane] * i + j * bpp / 8;
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;
}
}
}
}
static 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;
}
static void vc4_fb_convert_plane_to_sand_tiled(struct igt_fb *dst, void *dst_buf,
struct igt_fb *src, void *src_buf,
unsigned int plane)
{
uint64_t modifier_base = fourcc_mod_broadcom_mod(dst->modifier);
uint32_t column_height = fourcc_mod_broadcom_param(dst->modifier);
uint32_t column_width_bytes, column_width, column_size;
size_t bpp = dst->plane_bpp[plane];
unsigned int i, j;
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);
}
column_width = column_width_bytes * dst->plane_width[plane] / dst->width;
column_size = column_width_bytes * column_height;
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);
}
}
}
}
static void vc4_fb_convert_plane_from_sand_tiled(struct igt_fb *dst, void *dst_buf,
struct igt_fb *src, void *src_buf,
unsigned int plane)
{
uint64_t modifier_base = fourcc_mod_broadcom_mod(src->modifier);
uint32_t column_height = fourcc_mod_broadcom_param(src->modifier);
uint32_t column_width_bytes, column_width, column_size;
size_t bpp = src->plane_bpp[plane];
unsigned int i, j;
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);
}
column_width = column_width_bytes * src->plane_width[plane] / src->width;
column_size = column_width_bytes * column_height;
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 += vc4_sand_tiled_offset(column_width,
column_size, j, i,
bpp);
dst_offset += dst->strides[plane] * i + j * bpp / 8;
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);
}
}
}
}
void vc4_fb_convert_plane_to_tiled(struct igt_fb *dst, void *dst_buf,
struct igt_fb *src, void *src_buf)
{
unsigned int plane;
igt_assert(src->modifier == DRM_FORMAT_MOD_LINEAR);
igt_assert(igt_vc4_is_tiled(dst->modifier));
for (plane = 0; plane < src->num_planes; plane++) {
if (dst->modifier == DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED)
vc4_fb_convert_plane_to_t_tiled(dst, dst_buf, src, src_buf, plane);
else
vc4_fb_convert_plane_to_sand_tiled(dst, dst_buf, src, src_buf, plane);
}
}
void vc4_fb_convert_plane_from_tiled(struct igt_fb *dst, void *dst_buf,
struct igt_fb *src, void *src_buf)
{
unsigned int plane;
igt_assert(igt_vc4_is_tiled(src->modifier));
igt_assert(dst->modifier == DRM_FORMAT_MOD_LINEAR);
for (plane = 0; plane < src->num_planes; plane++) {
if (src->modifier == DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED)
vc4_fb_convert_plane_from_t_tiled(dst, dst_buf, src, src_buf, plane);
else
vc4_fb_convert_plane_from_sand_tiled(dst, dst_buf, src, src_buf, plane);
}
}