[igt-dev] [PATCH i-g-t 04/10] lib: Add engine copy support for YUV formats

Imre Deak imre.deak at intel.com
Mon Dec 30 03:40:34 UTC 2019


Add the missing bits to the Vebox copy and AUX pagetable helpers for
copying YUV FBs with the Vebox engine.

Cc: Mika Kahola <mika.kahola at intel.com>
Signed-off-by: Imre Deak <imre.deak at intel.com>
---
 lib/igt_fb.c            |  79 ++++++++++++++++++++++++--
 lib/intel_aux_pgtable.c | 121 ++++++++++++++++++++++++++++++++++++----
 lib/intel_batchbuffer.h |   4 ++
 lib/veboxcopy_gen12.c   |  58 ++++++++++++++++---
 4 files changed, 238 insertions(+), 24 deletions(-)

diff --git a/lib/igt_fb.c b/lib/igt_fb.c
index cc0fb373..e6a3ff07 100644
--- a/lib/igt_fb.c
+++ b/lib/igt_fb.c
@@ -359,6 +359,13 @@ static const struct format_desc_struct *lookup_drm_format(uint32_t drm_format)
 	return NULL;
 }
 
+static bool igt_format_is_yuv_semiplanar(uint32_t format)
+{
+	const struct format_desc_struct *f = lookup_drm_format(format);
+
+	return igt_format_is_yuv(format) && f->num_planes == 2;
+}
+
 /**
  * igt_get_fb_tile_size:
  * @fd: the DRM file descriptor
@@ -1967,19 +1974,56 @@ static bool use_blitter(const struct igt_fb *fb)
 		blitter_ok(fb);
 }
 
+static void init_buf_ccs(struct igt_buf *buf, int ccs_idx,
+			 uint32_t offset, uint32_t stride)
+{
+	buf->ccs[ccs_idx].offset = offset;
+	buf->ccs[ccs_idx].stride = stride;
+}
+
+static void init_buf_surface(struct igt_buf *buf, int surface_idx,
+			     uint32_t offset, uint32_t stride, uint32_t size)
+{
+	buf->surface[surface_idx].offset = offset;
+	buf->surface[surface_idx].stride = stride;
+	buf->surface[surface_idx].size = size;
+}
+
+static int yuv_semiplanar_bpp(uint32_t drm_format)
+{
+	switch (drm_format) {
+	case DRM_FORMAT_NV12:
+		return 8;
+	case DRM_FORMAT_P010:
+		return 10;
+	case DRM_FORMAT_P012:
+		return 12;
+	case DRM_FORMAT_P016:
+		return 16;
+	default:
+		igt_assert_f(0, "Unsupported format: %08x\n", drm_format);
+	}
+}
+
 static void init_buf(struct fb_blit_upload *blit,
 		     struct igt_buf *buf,
 		     const struct igt_fb *fb,
 		     const char *name)
 {
+	int num_surfaces;
+	int i;
+
 	igt_assert_eq(fb->offsets[0], 0);
 
 	buf->bo = gem_handle_to_libdrm_bo(blit->bufmgr, blit->fd,
 					  name, fb->gem_handle);
 	buf->tiling = igt_fb_mod_to_tiling(fb->modifier);
-	buf->surface[0].stride = fb->strides[0];
 	buf->bpp = fb->plane_bpp[0];
-	buf->surface[0].size = fb->size;
+	buf->format_is_yuv = igt_format_is_yuv(fb->drm_format);
+	buf->format_is_yuv_semiplanar =
+		igt_format_is_yuv_semiplanar(fb->drm_format);
+	if (buf->format_is_yuv_semiplanar)
+		buf->yuv_semiplanar_bpp = yuv_semiplanar_bpp(fb->drm_format);
 
 	if (is_ccs_modifier(fb->modifier)) {
 		igt_assert_eq(fb->strides[0] & 127, 0);
@@ -1994,8 +2038,24 @@ static void init_buf(struct fb_blit_upload *blit,
 		else
 			buf->compression = I915_COMPRESSION_RENDER;
 
-		buf->ccs[0].offset = fb->offsets[1];
-		buf->ccs[0].stride = fb->strides[1];
+		num_surfaces = fb->num_planes / 2;
+		for (i = 0; i < num_surfaces; i++)
+			init_buf_ccs(buf, i,
+				     fb->offsets[num_surfaces + i],
+				     fb->strides[num_surfaces + i]);
+	} else {
+		num_surfaces = fb->num_planes;
+	}
+
+	igt_assert(fb->offsets[0] == 0);
+	for (i = 0; i < num_surfaces; i++) {
+		uint32_t end =
+			i == fb->num_planes - 1 ? fb->size : fb->offsets[i + 1];
+
+		init_buf_surface(buf, i,
+				 fb->offsets[i],
+				 fb->strides[i],
+				 end - fb->offsets[i]);
 	}
 
 	if (fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC)
@@ -2007,6 +2067,15 @@ static void fini_buf(struct igt_buf *buf)
 	drm_intel_bo_unreference(buf->bo);
 }
 
+static bool use_vebox_copy(const struct igt_fb *src_fb,
+			   const struct igt_fb *dst_fb)
+{
+
+	return is_gen12_mc_ccs_modifier(dst_fb->modifier) ||
+	       igt_format_is_yuv(src_fb->drm_format) ||
+	       igt_format_is_yuv(dst_fb->drm_format);
+}
+
 /**
  * copy_with_engine:
  * @blit: context for the copy operation
@@ -2029,7 +2098,7 @@ static void copy_with_engine(struct fb_blit_upload *blit,
 	igt_render_copyfunc_t render_copy = NULL;
 	igt_vebox_copyfunc_t vebox_copy = NULL;
 
-	if (is_gen12_mc_ccs_modifier(dst_fb->modifier))
+	if (use_vebox_copy(src_fb, dst_fb))
 		vebox_copy = igt_get_vebox_copyfunc(intel_get_drm_devid(blit->fd));
 	else
 		render_copy = igt_get_render_copyfunc(intel_get_drm_devid(blit->fd));
diff --git a/lib/intel_aux_pgtable.c b/lib/intel_aux_pgtable.c
index 5addb2e2..fcd24f08 100644
--- a/lib/intel_aux_pgtable.c
+++ b/lib/intel_aux_pgtable.c
@@ -33,6 +33,12 @@
 
 #define max(a, b)		((a) > (b) ? (a) : (b))
 
+#define AUX_FORMAT_YCRCB	0x03
+#define AUX_FORMAT_P010		0x07
+#define AUX_FORMAT_P016		0x08
+#define AUX_FORMAT_ARGB_8B	0x0A
+#define AUX_FORMAT_NV12_21	0x0F
+
 struct pgtable_level_desc {
 	int idx_shift;
 	int idx_bits;
@@ -55,6 +61,23 @@ struct pgtable {
 	drm_intel_bo *bo;
 };
 
+static uint64_t last_buf_surface_end(const struct igt_buf *buf)
+{
+	uint64_t end_offset = 0;
+	int num_surfaces = buf->format_is_yuv_semiplanar ? 2 : 1;
+	int i;
+
+	for (i = 0; i < num_surfaces; i++) {
+		uint64_t surface_end = buf->surface[i].offset +
+				       buf->surface[i].size;
+
+		if (surface_end > end_offset)
+			end_offset = surface_end;
+	}
+
+	return end_offset;
+}
+
 static int
 pgt_table_count(int address_bits, const struct igt_buf **bufs, int buf_count)
 {
@@ -77,7 +100,7 @@ pgt_table_count(int address_bits, const struct igt_buf **bufs, int buf_count)
 		/* Avoid double counting for overlapping aligned bufs. */
 		start = max(start, end);
 
-		end = ALIGN(buf->bo->offset64 + buf->surface[0].size,
+		end = ALIGN(buf->bo->offset64 + last_buf_surface_end(buf),
 			    1UL << address_bits);
 		igt_assert(end >= start);
 
@@ -189,7 +212,29 @@ pgt_set_l1_entry(struct pgtable *pgt, uint64_t l1_table,
 	*l1_entry_ptr = ptr | flags;
 }
 
-static uint64_t pgt_get_l1_flags(const struct igt_buf *buf)
+#define DEPTH_VAL_RESERVED	3
+
+static int bpp_to_depth_val(int bpp)
+{
+	switch (bpp) {
+	case 8:
+		return 4;
+	case 10:
+		return 1;
+	case 12:
+		return 2;
+	case 16:
+		return 0;
+	case 32:
+		return 5;
+	case 64:
+		return 6;
+	default:
+		igt_assert_f(0, "invalid bpp %d\n", bpp);
+	}
+}
+
+static uint64_t pgt_get_l1_flags(const struct igt_buf *buf, int surface_idx)
 {
 	/*
 	 * The offset of .tile_mode isn't specifed by bspec, it's what Mesa
@@ -213,8 +258,6 @@ static uint64_t pgt_get_l1_flags(const struct igt_buf *buf)
 		.e = {
 			.valid = 1,
 			.tile_mode = buf->tiling == I915_TILING_Y ? 1 : 0,
-			.depth = 5,		/* 32bpp */
-			.format = 0xA,		/* B8G8R8A8_UNORM */
 		}
 	};
 
@@ -227,7 +270,49 @@ static uint64_t pgt_get_l1_flags(const struct igt_buf *buf)
 		   buf->tiling == I915_TILING_Yf ||
 		   buf->tiling == I915_TILING_Ys);
 
-	igt_assert(buf->bpp == 32);
+	entry.e.ycr = surface_idx > 0;
+
+	if (buf->format_is_yuv_semiplanar) {
+		entry.e.depth = bpp_to_depth_val(buf->bpp);
+		switch (buf->yuv_semiplanar_bpp) {
+		case 8:
+			entry.e.format = AUX_FORMAT_NV12_21;
+			entry.e.depth = DEPTH_VAL_RESERVED;
+			break;
+		case 10:
+			entry.e.format = AUX_FORMAT_P010;
+			entry.e.depth = bpp_to_depth_val(10);
+			break;
+		case 12:
+			entry.e.format = AUX_FORMAT_P016;
+			entry.e.depth = bpp_to_depth_val(12);
+			break;
+		case 16:
+			entry.e.format = AUX_FORMAT_P016;
+			entry.e.depth = bpp_to_depth_val(16);
+			break;
+		default:
+			igt_assert(0);
+		}
+	} else if (buf->format_is_yuv) {
+		switch (buf->bpp) {
+		case 16:
+			entry.e.format = AUX_FORMAT_YCRCB;
+			entry.e.depth = DEPTH_VAL_RESERVED;
+			break;
+		default:
+			igt_assert(0);
+		}
+	} else {
+		switch (buf->bpp) {
+		case 32:
+			entry.e.format = AUX_FORMAT_ARGB_8B;
+			entry.e.depth = bpp_to_depth_val(32);
+			break;
+		default:
+			igt_assert(0);
+		}
+	}
 
 	return entry.l;
 }
@@ -253,14 +338,21 @@ static uint64_t pgt_get_lx_flags(void)
 static void
 pgt_populate_entries_for_buf(struct pgtable *pgt,
 			       const struct igt_buf *buf,
-			       uint64_t top_table)
+			       uint64_t top_table,
+			       int surface_idx)
 {
-	uint64_t surface_addr = buf->bo->offset64;
-	uint64_t surface_end = surface_addr + buf->surface[0].size;
-	uint64_t aux_addr = buf->bo->offset64 + buf->ccs[0].offset;
-	uint64_t l1_flags = pgt_get_l1_flags(buf);
+	uint64_t surface_addr = buf->bo->offset64 +
+				buf->surface[surface_idx].offset;
+	uint64_t surface_end = surface_addr +
+			       buf->surface[surface_idx].size;
+	uint64_t aux_addr = buf->bo->offset64 + buf->ccs[surface_idx].offset;
+	uint64_t l1_flags = pgt_get_l1_flags(buf, surface_idx);
 	uint64_t lx_flags = pgt_get_lx_flags();
 
+	igt_assert(!(buf->surface[surface_idx].stride % 512));
+	igt_assert_eq(buf->ccs[surface_idx].stride,
+		      buf->surface[surface_idx].stride / 512 * 64);
+
 	for (; surface_addr < surface_end;
 	     surface_addr += MAIN_SURFACE_BLOCK_SIZE,
 	     aux_addr += AUX_CCS_BLOCK_SIZE) {
@@ -292,8 +384,13 @@ static void pgt_populate_entries(struct pgtable *pgt,
 	/* Top level table must be at offset 0. */
 	igt_assert(top_table == 0);
 
-	for (i = 0; i < buf_count; i++)
-		pgt_populate_entries_for_buf(pgt, bufs[i], top_table);
+	for (i = 0; i < buf_count; i++) {
+		igt_assert_eq(bufs[i]->surface[0].offset, 0);
+
+		pgt_populate_entries_for_buf(pgt, bufs[i], top_table, 0);
+		if (bufs[i]->format_is_yuv_semiplanar)
+			pgt_populate_entries_for_buf(pgt, bufs[i], top_table, 1);
+	}
 }
 
 static struct pgtable *
diff --git a/lib/intel_batchbuffer.h b/lib/intel_batchbuffer.h
index 69580839..fd7ef03f 100644
--- a/lib/intel_batchbuffer.h
+++ b/lib/intel_batchbuffer.h
@@ -235,8 +235,12 @@ struct igt_buf {
 	uint32_t tiling;
 	enum i915_compression compression;
 	uint32_t bpp;
+	uint32_t yuv_semiplanar_bpp;
 	uint32_t *data;
+	bool format_is_yuv:1;
+	bool format_is_yuv_semiplanar:1;
 	struct {
+		uint32_t offset;
 		uint32_t stride;
 		uint32_t size;
 	} surface[2];
diff --git a/lib/veboxcopy_gen12.c b/lib/veboxcopy_gen12.c
index 2f017514..237c43f2 100644
--- a/lib/veboxcopy_gen12.c
+++ b/lib/veboxcopy_gen12.c
@@ -26,7 +26,10 @@
 #include "intel_aux_pgtable.h"
 #include "veboxcopy.h"
 
+#define YCRCB_NORMAL	0
+#define PLANAR_420_8	4
 #define R8G8B8A8_UNORM	8
+#define PLANAR_420_16	12
 
 struct vebox_surface_state {
 	struct {
@@ -129,10 +132,23 @@ struct vebox_tiling_convert {
 	};
 } __attribute__((packed));
 
+static bool format_is_interleaved_yuv(int format)
+{
+	switch (format) {
+	case YCRCB_NORMAL:
+	case PLANAR_420_8:
+	case PLANAR_420_16:
+		return true;
+	}
+
+	return false;
+}
+
 static void emit_surface_state_cmd(struct intel_batchbuffer *batch,
 				   int surface_id,
 				   int width, int height, int bpp,
-				   int pitch, uint32_t tiling, int format)
+				   int pitch, uint32_t tiling, int format,
+				   uint32_t uv_offset)
 {
 	struct vebox_surface_state *ss;
 
@@ -149,11 +165,15 @@ static void emit_surface_state_cmd(struct intel_batchbuffer *batch,
 	ss->ss2.width = width - 1;
 
 	ss->ss3.surface_format = format;
+	if (format_is_interleaved_yuv(format))
+		ss->ss3.chroma_interleave = 1;
 	ss->ss3.surface_pitch = pitch - 1;
 	ss->ss3.tile_walk = (tiling == I915_TILING_Y) ||
 			    (tiling == I915_TILING_Yf);
 	ss->ss3.tiled_surface = tiling != I915_TILING_NONE;
 
+	ss->ss4.u_y_offset = uv_offset / pitch;
+
 	ss->ss7.derived_surface_pitch = pitch - 1;
 }
 
@@ -226,8 +246,7 @@ void gen12_vebox_copyfunc(struct intel_batchbuffer *batch,
 {
 	struct aux_pgtable_info aux_pgtable_info = { };
 	uint32_t aux_pgtable_state;
-
-	igt_assert(src->bpp == dst->bpp);
+	int format;
 
 	intel_batchbuffer_flush_on_ring(batch, I915_EXEC_VEBOX);
 
@@ -245,18 +264,43 @@ void gen12_vebox_copyfunc(struct intel_batchbuffer *batch,
 
 	gen12_emit_aux_pgtable_state(batch, aux_pgtable_state, false);
 
+	/* The tiling convert command can't convert formats. */
+	igt_assert_eq(src->format_is_yuv, dst->format_is_yuv);
+	igt_assert_eq(src->format_is_yuv_semiplanar,
+		      dst->format_is_yuv_semiplanar);
+	igt_assert_eq(src->bpp, dst->bpp);
+
 	/* TODO: add support for more formats */
-	igt_assert(src->bpp == 32);
+	switch (src->bpp) {
+	case 8:
+		igt_assert(src->format_is_yuv_semiplanar);
+		format = PLANAR_420_8;
+		break;
+	case 16:
+		igt_assert(src->format_is_yuv);
+		format = src->format_is_yuv_semiplanar ? PLANAR_420_16 :
+							 YCRCB_NORMAL;
+		break;
+	case 32:
+		igt_assert(!src->format_is_yuv &&
+			   !src->format_is_yuv_semiplanar);
+		format = R8G8B8A8_UNORM;
+		break;
+	default:
+		igt_assert_f(0, "Unsupported bpp: %u\n", src->bpp);
+	}
+
+	igt_assert(!src->format_is_yuv_semiplanar ||
+		   (src->surface[1].offset && dst->surface[1].offset));
 	emit_surface_state_cmd(batch, VEBOX_SURFACE_INPUT,
 			       width, height, src->bpp,
 			       src->surface[0].stride,
-			       src->tiling, R8G8B8A8_UNORM);
+			       src->tiling, format, src->surface[1].offset);
 
-	igt_assert(dst->bpp == 32);
 	emit_surface_state_cmd(batch, VEBOX_SURFACE_OUTPUT,
 			       width, height, dst->bpp,
 			       dst->surface[0].stride,
-			       dst->tiling, R8G8B8A8_UNORM);
+			       dst->tiling, format, dst->surface[1].offset);
 
 	emit_tiling_convert_cmd(batch,
 				src->bo, src->tiling, src->compression,
-- 
2.23.1



More information about the igt-dev mailing list