[i-g-t,v2,03/13] igt: fb: generic YUV convertion function

Submitted by Maxime Ripard on Jan. 8, 2019, 3:19 p.m.

Details

Message ID 20190108152001.5367-3-maxime.ripard@bootlin.com
State New
Series "igt: chamelium: Test YUV buffers using the Chamelium"
Headers show

Commit Message

Maxime Ripard Jan. 8, 2019, 3:19 p.m.
The current way we work when we want to support a new YUV format to IGT, we
also need to add new function to convert to and from XRGB8888.

This doesn't really scale however, and creates a lot of code to maintain.
In order to work around this, create a generic function to convert to RGB
and one to convert from RGB.

The only thing that is needed now is to add new parameters, and that's it.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 lib/igt_fb.c | 537 ++++++++++++++++-----------------------------------
 1 file changed, 171 insertions(+), 366 deletions(-)

Patch hide | download patch | download mbox

diff --git a/lib/igt_fb.c b/lib/igt_fb.c
index 6ab4e3039166..45691fd441d9 100644
--- a/lib/igt_fb.c
+++ b/lib/igt_fb.c
@@ -1577,424 +1577,237 @@  static void convert_src_put(const struct fb_convert *cvt,
 		free(src_buf);
 }
 
-static void convert_nv12_to_rgb24(struct fb_convert *cvt)
+struct yuv_parameters {
+	unsigned	y_inc;
+	unsigned	uv_inc;
+	unsigned	y_stride;
+	unsigned	uv_stride;
+	unsigned	y_offset;
+	unsigned	u_offset;
+	unsigned	v_offset;
+};
+
+static void get_yuv_parameters(struct igt_fb *fb, struct yuv_parameters *params)
 {
-	int i, j;
-	const uint8_t *y, *uv;
-	uint8_t *rgb24 = cvt->dst.ptr;
-	unsigned int rgb24_stride = cvt->dst.fb->strides[0];
-	unsigned int planar_stride = cvt->src.fb->strides[0];
-	struct igt_mat4 m = igt_ycbcr_to_rgb_matrix(cvt->src.fb->color_encoding,
-						    cvt->src.fb->color_range);
-	uint8_t *buf;
-
-	igt_assert(cvt->src.fb->drm_format == DRM_FORMAT_NV12 &&
-		   cvt->dst.fb->drm_format == DRM_FORMAT_XRGB8888);
-
-	buf = convert_src_get(cvt);
-	y = buf + cvt->src.fb->offsets[0];
-	uv = buf + cvt->src.fb->offsets[1];
-
-	for (i = 0; i < cvt->dst.fb->height / 2; i++) {
-		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
-			/* Convert 2x2 pixel blocks */
-			struct igt_vec4 yuv[4];
-			struct igt_vec4 rgb[4];
-
-			yuv[0].d[0] = y[j * 2 + 0];
-			yuv[1].d[0] = y[j * 2 + 1];
-			yuv[2].d[0] = y[j * 2 + 0 + planar_stride];
-			yuv[3].d[0] = y[j * 2 + 1 + planar_stride];
-
-			yuv[0].d[1] = yuv[1].d[1] = yuv[2].d[1] = yuv[3].d[1] = uv[j * 2 + 0];
-			yuv[0].d[2] = yuv[1].d[2] = yuv[2].d[2] = yuv[3].d[2] = uv[j * 2 + 1];
-			yuv[0].d[3] = yuv[1].d[3] = yuv[2].d[3] = yuv[3].d[3] = 1.0f;
-
-			rgb[0] = igt_matrix_transform(&m, &yuv[0]);
-			rgb[1] = igt_matrix_transform(&m, &yuv[1]);
-			rgb[2] = igt_matrix_transform(&m, &yuv[2]);
-			rgb[3] = igt_matrix_transform(&m, &yuv[3]);
-
-			write_rgb(&rgb24[j * 8 + 0], &rgb[0]);
-			write_rgb(&rgb24[j * 8 + 4], &rgb[1]);
-			write_rgb(&rgb24[j * 8 + 0 + rgb24_stride], &rgb[2]);
-			write_rgb(&rgb24[j * 8 + 4 + rgb24_stride], &rgb[3]);
-		}
-
-		if (cvt->dst.fb->width & 1) {
-			/* Convert 1x2 pixel block */
-			struct igt_vec4 yuv[2];
-			struct igt_vec4 rgb[2];
-
-			yuv[0].d[0] = y[j * 2 + 0];
-			yuv[1].d[0] = y[j * 2 + 0 + planar_stride];
+	igt_assert(igt_format_is_yuv(fb->drm_format));
 
-			yuv[0].d[1] = yuv[1].d[1] = uv[j * 2 + 0];
-			yuv[0].d[2] = yuv[1].d[2] = uv[j * 2 + 1];
-			yuv[0].d[3] = yuv[1].d[3] = 1.0f;
+	switch (fb->drm_format) {
+	case DRM_FORMAT_NV12:
+		params->y_inc = 1;
+		params->uv_inc = 2;
+		break;
 
-			rgb[0] = igt_matrix_transform(&m, &yuv[0]);
-			rgb[1] = igt_matrix_transform(&m, &yuv[1]);
-
-			write_rgb(&rgb24[j * 8 + 0], &rgb[0]);
-			write_rgb(&rgb24[j * 8 + 0 + rgb24_stride], &rgb[1]);
-		}
-
-		rgb24 += 2 * rgb24_stride;
-		y += 2 * planar_stride;
-		uv += planar_stride;
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_UYVY:
+	case DRM_FORMAT_VYUY:
+		params->y_inc = 2;
+		params->uv_inc = 4;
+		break;
+
+	case DRM_FORMAT_XYUV8888:
+		params->y_inc = 4;
+		params->uv_inc = 4;
+		break;
 	}
 
-	if (cvt->dst.fb->height & 1) {
-		/* Convert last row */
-		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
-			/* Convert 2x1 pixel blocks */
-			struct igt_vec4 yuv[2];
-			struct igt_vec4 rgb[2];
-
-			yuv[0].d[0] = y[j * 2 + 0];
-			yuv[1].d[0] = y[j * 2 + 1];
-			yuv[0].d[1] = yuv[1].d[1] = uv[j * 2 + 0];
-			yuv[0].d[2] = yuv[1].d[2] = uv[j * 2 + 1];
-			yuv[0].d[3] = yuv[1].d[3] = 1.0f;
+	switch (fb->drm_format) {
+	case DRM_FORMAT_NV12:
+		params->y_stride = fb->strides[0];
+		params->uv_stride = fb->strides[1];
+		break;
 
-			rgb[0] = igt_matrix_transform(&m, &yuv[0]);
-			rgb[1] = igt_matrix_transform(&m, &yuv[1]);
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_UYVY:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_XYUV8888:
+		params->y_stride = fb->strides[0];
+		params->uv_stride = fb->strides[0];
+		break;
+	}
 
-			write_rgb(&rgb24[j * 8 + 0], &rgb[0]);
-			write_rgb(&rgb24[j * 8 + 4], &rgb[0]);
-		}
+	switch (fb->drm_format) {
+	case DRM_FORMAT_NV12:
+		params->y_offset = fb->offsets[0];
+		params->u_offset = fb->offsets[1];
+		params->v_offset = fb->offsets[1] + 1;
+		break;
 
-		if (cvt->dst.fb->width & 1) {
-			/* Convert single pixel */
-			struct igt_vec4 yuv;
-			struct igt_vec4 rgb;
+	case DRM_FORMAT_YUYV:
+		params->y_offset = fb->offsets[0];
+		params->u_offset = fb->offsets[0] + 1;
+		params->v_offset = fb->offsets[0] + 3;
+		break;
 
-			yuv.d[0] = y[j * 2 + 0];
-			yuv.d[1] = uv[j * 2 + 0];
-			yuv.d[2] = uv[j * 2 + 1];
-			yuv.d[3] = 1.0f;
+	case DRM_FORMAT_YVYU:
+		params->y_offset = fb->offsets[0];
+		params->u_offset = fb->offsets[0] + 3;
+		params->v_offset = fb->offsets[0] + 1;
+		break;
 
-			rgb = igt_matrix_transform(&m, &yuv);
+	case DRM_FORMAT_UYVY:
+		params->y_offset = fb->offsets[0] + 1;
+		params->u_offset = fb->offsets[0];
+		params->v_offset = fb->offsets[0] + 2;
+		break;
 
-			write_rgb(&rgb24[j * 8 + 0], &rgb);
-		}
+	case DRM_FORMAT_VYUY:
+		params->y_offset = fb->offsets[0] + 1;
+		params->u_offset = fb->offsets[0] + 2;
+		params->v_offset = fb->offsets[0];
+		break;
+
+	case DRM_FORMAT_XYUV8888:
+		params->y_offset = fb->offsets[0] + 1;
+		params->u_offset = fb->offsets[0] + 2;
+		params->v_offset = fb->offsets[0] + 3;
+		break;
 	}
-
-	convert_src_put(cvt, buf);
 }
 
-static void convert_yuv444_to_rgb24(struct fb_convert *cvt)
+static void convert_yuv_to_rgb24(struct fb_convert *cvt)
 {
+	const struct format_desc_struct *src_fmt =
+		lookup_drm_format(cvt->src.fb->drm_format);
 	int i, j;
-	uint8_t *yuv24;
+	uint8_t bpp = 4;
+	uint8_t *y, *u, *v;
 	uint8_t *rgb24 = cvt->dst.ptr;
-	unsigned rgb24_stride = cvt->dst.fb->strides[0], xyuv_stride = cvt->src.fb->strides[0];
-	uint8_t *buf = malloc(cvt->src.fb->size);
+	unsigned int rgb24_stride = cvt->dst.fb->strides[0];
 	struct igt_mat4 m = igt_ycbcr_to_rgb_matrix(cvt->src.fb->color_encoding,
 						    cvt->src.fb->color_range);
+	uint8_t *buf;
+	struct yuv_parameters params = { };
 
-	/*
-	 * Reading from the BO is awfully slow because of lack of read caching,
-	 * it's faster to copy the whole BO to a temporary buffer and convert
-	 * from there.
-	 */
-	igt_memcpy_from_wc(buf, cvt->src.ptr + cvt->src.fb->offsets[0], cvt->src.fb->size);
-	yuv24 = buf;
+	igt_assert(cvt->dst.fb->drm_format == DRM_FORMAT_XRGB8888 &&
+		   igt_format_is_yuv(cvt->src.fb->drm_format));
+
+	buf = convert_src_get(cvt);
+	get_yuv_parameters(cvt->src.fb, &params);
+	y = buf + params.y_offset;
+	u = buf + params.u_offset;
+	v = buf + params.v_offset;
 
 	for (i = 0; i < cvt->dst.fb->height; i++) {
+		const uint8_t *y_tmp = y;
+		const uint8_t *u_tmp = u;
+		const uint8_t *v_tmp = v;
+		uint8_t *rgb_tmp = rgb24;
+
 		for (j = 0; j < cvt->dst.fb->width; j++) {
-			float y, u, v;
-			struct igt_vec4 yuv;
-			struct igt_vec4 rgb;
-
-			v = yuv24[i * xyuv_stride + j * 4];
-			u = yuv24[i * xyuv_stride + j * 4 + 1];
-			y = yuv24[i * xyuv_stride + j * 4 + 2];
-			yuv.d[0] = y;
-			yuv.d[1] = u;
-			yuv.d[2] = v;
+			struct igt_vec4 rgb, yuv;
+
+			yuv.d[0] = *y_tmp;
+			yuv.d[1] = *u_tmp;
+			yuv.d[2] = *v_tmp;
 			yuv.d[3] = 1.0f;
 
 			rgb = igt_matrix_transform(&m, &yuv);
+			write_rgb(rgb_tmp, &rgb);
 
-			write_rgb(&rgb24[i * rgb24_stride + j * 4], &rgb);
-		}
-	}
-
-	free(buf);
-}
-
-
-static void convert_rgb24_to_yuv444(struct fb_convert *cvt)
-{
-	int i, j;
-	uint8_t *rgb24;
-	uint8_t *yuv444 = cvt->dst.ptr + cvt->dst.fb->offsets[0];
-	unsigned int rgb24_stride = cvt->src.fb->strides[0], xyuv_stride = cvt->dst.fb->strides[0];
-	struct igt_mat4 m = igt_rgb_to_ycbcr_matrix(cvt->dst.fb->color_encoding,
-						    cvt->dst.fb->color_range);
-
-	rgb24 = cvt->src.ptr;
+			rgb_tmp += bpp;
+			y_tmp += params.y_inc;
 
-	igt_assert_f(cvt->dst.fb->drm_format == DRM_FORMAT_XYUV8888,
-		     "Conversion not implemented for !XYUV packed formats\n");
-
-	for (i = 0; i < cvt->dst.fb->height; i++) {
-		for (j = 0; j < cvt->dst.fb->width; j++) {
-			struct igt_vec4 rgb;
-			struct igt_vec4 yuv;
-
-			read_rgb(&rgb, &rgb24[i * rgb24_stride + j * 4]);
+			if ((src_fmt->hsub == 1) || (j % src_fmt->hsub)) {
+				u_tmp += params.uv_inc;
+				v_tmp += params.uv_inc;
+			}
+		}
 
-			yuv = igt_matrix_transform(&m, &rgb);
+		rgb24 += rgb24_stride;
+		y += params.y_stride;
 
-			yuv444[i * xyuv_stride + j * 4] = yuv.d[2];
-			yuv444[i * xyuv_stride + j * 4 + 1] = yuv.d[1];
-			yuv444[i * xyuv_stride + j * 4 + 2] = yuv.d[0];
+		if ((src_fmt->vsub == 1) || (i % src_fmt->vsub)) {
+			u += params.uv_stride;
+			v += params.uv_stride;
 		}
 	}
+
+	convert_src_put(cvt, buf);
 }
 
-static void convert_rgb24_to_nv12(struct fb_convert *cvt)
+static void convert_rgb24_to_yuv(struct fb_convert *cvt)
 {
+	const struct format_desc_struct *dst_fmt =
+		lookup_drm_format(cvt->dst.fb->drm_format);
 	int i, j;
-	uint8_t *y = cvt->dst.ptr + cvt->dst.fb->offsets[0];
-	uint8_t *uv = cvt->dst.ptr + cvt->dst.fb->offsets[1];
+	uint8_t *y, *u, *v;
 	const uint8_t *rgb24 = cvt->src.ptr;
+	uint8_t bpp = 4;
 	unsigned rgb24_stride = cvt->src.fb->strides[0];
-	unsigned planar_stride = cvt->dst.fb->strides[0];
 	struct igt_mat4 m = igt_rgb_to_ycbcr_matrix(cvt->dst.fb->color_encoding,
 						    cvt->dst.fb->color_range);
+	struct yuv_parameters params = { };
 
 	igt_assert(cvt->src.fb->drm_format == DRM_FORMAT_XRGB8888 &&
-		   cvt->dst.fb->drm_format == DRM_FORMAT_NV12);
+		   igt_format_is_yuv(cvt->dst.fb->drm_format));
 
-	for (i = 0; i < cvt->dst.fb->height / 2; i++) {
-		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
-			/* Convert 2x2 pixel blocks */
-			struct igt_vec4 rgb[4];
-			struct igt_vec4 yuv[4];
+	get_yuv_parameters(cvt->dst.fb, &params);
+	y = cvt->dst.ptr + params.y_offset;
+	u = cvt->dst.ptr + params.u_offset;
+	v = cvt->dst.ptr + params.v_offset;
 
-			read_rgb(&rgb[0], &rgb24[j * 8 + 0]);
-			read_rgb(&rgb[1], &rgb24[j * 8 + 4]);
-			read_rgb(&rgb[2], &rgb24[j * 8 + 0 + rgb24_stride]);
-			read_rgb(&rgb[3], &rgb24[j * 8 + 4 + rgb24_stride]);
-
-			yuv[0] = igt_matrix_transform(&m, &rgb[0]);
-			yuv[1] = igt_matrix_transform(&m, &rgb[1]);
-			yuv[2] = igt_matrix_transform(&m, &rgb[2]);
-			yuv[3] = igt_matrix_transform(&m, &rgb[3]);
-
-			y[j * 2 + 0] = yuv[0].d[0];
-			y[j * 2 + 1] = yuv[1].d[0];
-			y[j * 2 + 0 + planar_stride] = yuv[2].d[0];
-			y[j * 2 + 1 + planar_stride] = yuv[3].d[0];
+	for (i = 0; i < cvt->dst.fb->height; i++) {
+		const uint8_t *rgb_tmp = rgb24;
+		uint8_t *y_tmp = y;
+		uint8_t *u_tmp = u;
+		uint8_t *v_tmp = v;
 
-			/*
-			 * We assume the MPEG2 chroma siting convention, where
-			 * pixel center for Cb'Cr' is between the left top and
-			 * bottom pixel in a 2x2 block, so take the average.
-			 */
-			uv[j * 2 + 0] = (yuv[0].d[1] + yuv[2].d[1]) / 2.0f;
-			uv[j * 2 + 1] = (yuv[0].d[2] + yuv[2].d[2]) / 2.0f;
-		}
+		for (j = 0; j < cvt->dst.fb->width; j++) {
+			const uint8_t *pair_rgb24 = rgb_tmp;
+			struct igt_vec4 pair_rgb, rgb;
+			struct igt_vec4 pair_yuv, yuv;
 
-		if (cvt->dst.fb->width & 1) {
-			/* Convert 1x2 pixel block */
-			struct igt_vec4 rgb[2];
-			struct igt_vec4 yuv[2];
+			read_rgb(&rgb, rgb_tmp);
+			yuv = igt_matrix_transform(&m, &rgb);
 
-			read_rgb(&rgb[0], &rgb24[j * 8 + 0]);
-			read_rgb(&rgb[2], &rgb24[j * 8 + 0 + rgb24_stride]);
+			rgb_tmp += bpp;
 
-			yuv[0] = igt_matrix_transform(&m, &rgb[0]);
-			yuv[1] = igt_matrix_transform(&m, &rgb[1]);
+			*y_tmp = yuv.d[0];
+			y_tmp += params.y_inc;
 
-			y[j * 2 + 0] = yuv[0].d[0];
-			y[j * 2 + 0 + planar_stride] = yuv[1].d[0];
+			if ((i % dst_fmt->vsub) || (j % dst_fmt->hsub))
+				continue;
 
 			/*
 			 * We assume the MPEG2 chroma siting convention, where
 			 * pixel center for Cb'Cr' is between the left top and
 			 * bottom pixel in a 2x2 block, so take the average.
+			 *
+			 * Therefore, if we use subsampling, we only really care
+			 * about two pixels all the time, either the two
+			 * subsequent pixels horizontally, vertically, or the
+			 * two corners in a 2x2 block.
+			 *
+			 * The only corner case is when we have an odd number of
+			 * pixels, but this can be handled pretty easily by not
+			 * incrementing the paired pixel pointer in the
+			 * direction it's odd in.
 			 */
-			uv[j * 2 + 0] = (yuv[0].d[1] + yuv[1].d[1]) / 2.0f;
-			uv[j * 2 + 1] = (yuv[0].d[2] + yuv[1].d[2]) / 2.0f;
-		}
-
-		rgb24 += 2 * rgb24_stride;
-		y += 2 * planar_stride;
-		uv += planar_stride;
-	}
-
-	/* Last row cannot be interpolated between 2 pixels, take the single value */
-	if (cvt->dst.fb->height & 1) {
-		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
-			/* Convert 2x1 pixel blocks */
-			struct igt_vec4 rgb[2];
-			struct igt_vec4 yuv[2];
-
-			read_rgb(&rgb[0], &rgb24[j * 8 + 0]);
-			read_rgb(&rgb[1], &rgb24[j * 8 + 4]);
-
-			yuv[0] = igt_matrix_transform(&m, &rgb[0]);
-			yuv[1] = igt_matrix_transform(&m, &rgb[1]);
+			if (j != (cvt->dst.fb->width - 1))
+				pair_rgb24 += (dst_fmt->hsub - 1) * bpp;
 
-			y[j * 2 + 0] = yuv[0].d[0];
-			y[j * 2 + 1] = yuv[1].d[0];
-			uv[j * 2 + 0] = yuv[0].d[1];
-			uv[j * 2 + 1] = yuv[0].d[2];
-		}
-
-		if (cvt->dst.fb->width & 1) {
-			/* Convert single pixel */
-			struct igt_vec4 rgb;
-			struct igt_vec4 yuv;
+			if (i != (cvt->dst.fb->height - 1))
+				pair_rgb24 += rgb24_stride * (dst_fmt->vsub - 1);
 
-			read_rgb(&rgb, &rgb24[j * 8 + 0]);
+			read_rgb(&pair_rgb, pair_rgb24);
+			pair_yuv = igt_matrix_transform(&m, &pair_rgb);
 
-			yuv = igt_matrix_transform(&m, &rgb);
+			*u_tmp = (yuv.d[1] + pair_yuv.d[1]) / 2.0f;
+			*v_tmp = (yuv.d[2] + pair_yuv.d[2]) / 2.0f;
 
-			y[j * 2 + 0] = yuv.d[0];
-			uv[j * 2 + 0] = yuv.d[1];
-			uv[j * 2 + 1] = yuv.d[2];
-		}
-	}
-}
-
-/* { Y0, U, Y1, V } */
-static const unsigned char swizzle_yuyv[] = { 0, 1, 2, 3 };
-static const unsigned char swizzle_yvyu[] = { 0, 3, 2, 1 };
-static const unsigned char swizzle_uyvy[] = { 1, 0, 3, 2 };
-static const unsigned char swizzle_vyuy[] = { 1, 2, 3, 0 };
-
-static const unsigned char *yuyv_swizzle(uint32_t format)
-{
-	switch (format) {
-	default:
-	case DRM_FORMAT_YUYV:
-		return swizzle_yuyv;
-	case DRM_FORMAT_YVYU:
-		return swizzle_yvyu;
-	case DRM_FORMAT_UYVY:
-		return swizzle_uyvy;
-	case DRM_FORMAT_VYUY:
-		return swizzle_vyuy;
-	}
-}
-
-static void convert_yuyv_to_rgb24(struct fb_convert *cvt)
-{
-	int i, j;
-	const uint8_t *yuyv;
-	uint8_t *rgb24 = cvt->dst.ptr;
-	unsigned int rgb24_stride = cvt->dst.fb->strides[0];
-	unsigned int yuyv_stride = cvt->src.fb->strides[0];
-	struct igt_mat4 m = igt_ycbcr_to_rgb_matrix(cvt->src.fb->color_encoding,
-						    cvt->src.fb->color_range);
-	const unsigned char *swz = yuyv_swizzle(cvt->src.fb->drm_format);
-	uint8_t *buf;
-
-	igt_assert((cvt->src.fb->drm_format == DRM_FORMAT_YUYV ||
-		    cvt->src.fb->drm_format == DRM_FORMAT_UYVY ||
-		    cvt->src.fb->drm_format == DRM_FORMAT_YVYU ||
-		    cvt->src.fb->drm_format == DRM_FORMAT_VYUY) &&
-		   cvt->dst.fb->drm_format == DRM_FORMAT_XRGB8888);
-
-	buf = convert_src_get(cvt);
-	yuyv = buf;
-
-	for (i = 0; i < cvt->dst.fb->height; i++) {
-		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
-			/* Convert 2x1 pixel blocks */
-			struct igt_vec4 yuv[2];
-			struct igt_vec4 rgb[2];
-
-			yuv[0].d[0] = yuyv[j * 4 + swz[0]];
-			yuv[1].d[0] = yuyv[j * 4 + swz[2]];
-			yuv[0].d[1] = yuv[1].d[1] = yuyv[j * 4 + swz[1]];
-			yuv[0].d[2] = yuv[1].d[2] = yuyv[j * 4 + swz[3]];
-			yuv[0].d[3] = yuv[1].d[3] = 1.0f;
-
-			rgb[0] = igt_matrix_transform(&m, &yuv[0]);
-			rgb[1] = igt_matrix_transform(&m, &yuv[1]);
-
-			write_rgb(&rgb24[j * 8 + 0], &rgb[0]);
-			write_rgb(&rgb24[j * 8 + 4], &rgb[1]);
-		}
-
-		if (cvt->dst.fb->width & 1) {
-			struct igt_vec4 yuv;
-			struct igt_vec4 rgb;
-
-			yuv.d[0] = yuyv[j * 4 + swz[0]];
-			yuv.d[1] = yuyv[j * 4 + swz[1]];
-			yuv.d[2] = yuyv[j * 4 + swz[3]];
-			yuv.d[3] = 1.0f;
-
-			rgb = igt_matrix_transform(&m, &yuv);
-
-			write_rgb(&rgb24[j * 8 + 0], &rgb);
+			u_tmp += params.uv_inc;
+			v_tmp += params.uv_inc;
 		}
 
 		rgb24 += rgb24_stride;
-		yuyv += yuyv_stride;
-	}
-
-	convert_src_put(cvt, buf);
-}
-
-static void convert_rgb24_to_yuyv(struct fb_convert *cvt)
-{
-	int i, j;
-	uint8_t *yuyv = cvt->dst.ptr;
-	const uint8_t *rgb24 = cvt->src.ptr;
-	unsigned rgb24_stride = cvt->src.fb->strides[0];
-	unsigned yuyv_stride = cvt->dst.fb->strides[0];
-	struct igt_mat4 m = igt_rgb_to_ycbcr_matrix(cvt->dst.fb->color_encoding,
-						    cvt->dst.fb->color_range);
-	const unsigned char *swz = yuyv_swizzle(cvt->dst.fb->drm_format);
-
-	igt_assert(cvt->src.fb->drm_format == DRM_FORMAT_XRGB8888 &&
-		   (cvt->dst.fb->drm_format == DRM_FORMAT_YUYV ||
-		    cvt->dst.fb->drm_format == DRM_FORMAT_UYVY ||
-		    cvt->dst.fb->drm_format == DRM_FORMAT_YVYU ||
-		    cvt->dst.fb->drm_format == DRM_FORMAT_VYUY));
-
-	for (i = 0; i < cvt->dst.fb->height; i++) {
-		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
-			/* Convert 2x1 pixel blocks */
-			struct igt_vec4 rgb[2];
-			struct igt_vec4 yuv[2];
-
-			read_rgb(&rgb[0], &rgb24[j * 8 + 0]);
-			read_rgb(&rgb[1], &rgb24[j * 8 + 4]);
-
-			yuv[0] = igt_matrix_transform(&m, &rgb[0]);
-			yuv[1] = igt_matrix_transform(&m, &rgb[1]);
+		y += params.y_stride;
 
-			yuyv[j * 4 + swz[0]] = yuv[0].d[0];
-			yuyv[j * 4 + swz[2]] = yuv[1].d[0];
-			yuyv[j * 4 + swz[1]] = (yuv[0].d[1] + yuv[1].d[1]) / 2.0f;
-			yuyv[j * 4 + swz[3]] = (yuv[0].d[2] + yuv[1].d[2]) / 2.0f;
+		if ((i % dst_fmt->vsub) == (dst_fmt->vsub - 1)) {
+			u += params.uv_stride;
+			v += params.uv_stride;
 		}
-
-		if (cvt->dst.fb->width & 1) {
-			struct igt_vec4 rgb;
-			struct igt_vec4 yuv;
-
-			read_rgb(&rgb, &rgb24[j * 8 + 0]);
-
-			yuv = igt_matrix_transform(&m, &rgb);
-
-			yuyv[j * 4 + swz[0]] = yuv.d[0];
-			yuyv[j * 4 + swz[1]] = yuv.d[1];
-			yuyv[j * 4 + swz[3]] = yuv.d[2];
-		}
-
-		rgb24 += rgb24_stride;
-		yuyv += yuyv_stride;
 	}
 }
 
@@ -2046,31 +1859,23 @@  static void fb_convert(struct fb_convert *cvt)
 	} else if (cvt->dst.fb->drm_format == DRM_FORMAT_XRGB8888) {
 		switch (cvt->src.fb->drm_format) {
 		case DRM_FORMAT_XYUV8888:
-			convert_yuv444_to_rgb24(cvt);
-			return;
 		case DRM_FORMAT_NV12:
-			convert_nv12_to_rgb24(cvt);
-			return;
-		case DRM_FORMAT_YUYV:
-		case DRM_FORMAT_YVYU:
 		case DRM_FORMAT_UYVY:
 		case DRM_FORMAT_VYUY:
-			convert_yuyv_to_rgb24(cvt);
+		case DRM_FORMAT_YUYV:
+		case DRM_FORMAT_YVYU:
+			convert_yuv_to_rgb24(cvt);
 			return;
 		}
 	} else if (cvt->src.fb->drm_format == DRM_FORMAT_XRGB8888) {
 		switch (cvt->dst.fb->drm_format) {
 		case DRM_FORMAT_XYUV8888:
-			convert_rgb24_to_yuv444(cvt);
-			return;
 		case DRM_FORMAT_NV12:
-			convert_rgb24_to_nv12(cvt);
-			return;
 		case DRM_FORMAT_YUYV:
 		case DRM_FORMAT_YVYU:
 		case DRM_FORMAT_UYVY:
 		case DRM_FORMAT_VYUY:
-			convert_rgb24_to_yuyv(cvt);
+			convert_rgb24_to_yuv(cvt);
 			return;
 		}
 	}

Comments

Paul Kocialkowski Jan. 10, 2019, 10:07 a.m.
On Tue, 2019-01-08 at 16:19 +0100, Maxime Ripard wrote:
> The current way we work when we want to support a new YUV format to IGT, we
> also need to add new function to convert to and from XRGB8888.
> 
> This doesn't really scale however, and creates a lot of code to maintain.
> In order to work around this, create a generic function to convert to RGB
> and one to convert from RGB.
> 
> The only thing that is needed now is to add new parameters, and that's it.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

Reviewed-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>

> ---
>  lib/igt_fb.c | 537 ++++++++++++++++-----------------------------------
>  1 file changed, 171 insertions(+), 366 deletions(-)
> 
> diff --git a/lib/igt_fb.c b/lib/igt_fb.c
> index 6ab4e3039166..45691fd441d9 100644
> --- a/lib/igt_fb.c
> +++ b/lib/igt_fb.c
> @@ -1577,424 +1577,237 @@ static void convert_src_put(const struct fb_convert *cvt,
>  		free(src_buf);
>  }
>  
> -static void convert_nv12_to_rgb24(struct fb_convert *cvt)
> +struct yuv_parameters {
> +	unsigned	y_inc;
> +	unsigned	uv_inc;
> +	unsigned	y_stride;
> +	unsigned	uv_stride;
> +	unsigned	y_offset;
> +	unsigned	u_offset;
> +	unsigned	v_offset;
> +};
> +
> +static void get_yuv_parameters(struct igt_fb *fb, struct yuv_parameters *params)
>  {
> -	int i, j;
> -	const uint8_t *y, *uv;
> -	uint8_t *rgb24 = cvt->dst.ptr;
> -	unsigned int rgb24_stride = cvt->dst.fb->strides[0];
> -	unsigned int planar_stride = cvt->src.fb->strides[0];
> -	struct igt_mat4 m = igt_ycbcr_to_rgb_matrix(cvt->src.fb->color_encoding,
> -						    cvt->src.fb->color_range);
> -	uint8_t *buf;
> -
> -	igt_assert(cvt->src.fb->drm_format == DRM_FORMAT_NV12 &&
> -		   cvt->dst.fb->drm_format == DRM_FORMAT_XRGB8888);
> -
> -	buf = convert_src_get(cvt);
> -	y = buf + cvt->src.fb->offsets[0];
> -	uv = buf + cvt->src.fb->offsets[1];
> -
> -	for (i = 0; i < cvt->dst.fb->height / 2; i++) {
> -		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
> -			/* Convert 2x2 pixel blocks */
> -			struct igt_vec4 yuv[4];
> -			struct igt_vec4 rgb[4];
> -
> -			yuv[0].d[0] = y[j * 2 + 0];
> -			yuv[1].d[0] = y[j * 2 + 1];
> -			yuv[2].d[0] = y[j * 2 + 0 + planar_stride];
> -			yuv[3].d[0] = y[j * 2 + 1 + planar_stride];
> -
> -			yuv[0].d[1] = yuv[1].d[1] = yuv[2].d[1] = yuv[3].d[1] = uv[j * 2 + 0];
> -			yuv[0].d[2] = yuv[1].d[2] = yuv[2].d[2] = yuv[3].d[2] = uv[j * 2 + 1];
> -			yuv[0].d[3] = yuv[1].d[3] = yuv[2].d[3] = yuv[3].d[3] = 1.0f;
> -
> -			rgb[0] = igt_matrix_transform(&m, &yuv[0]);
> -			rgb[1] = igt_matrix_transform(&m, &yuv[1]);
> -			rgb[2] = igt_matrix_transform(&m, &yuv[2]);
> -			rgb[3] = igt_matrix_transform(&m, &yuv[3]);
> -
> -			write_rgb(&rgb24[j * 8 + 0], &rgb[0]);
> -			write_rgb(&rgb24[j * 8 + 4], &rgb[1]);
> -			write_rgb(&rgb24[j * 8 + 0 + rgb24_stride], &rgb[2]);
> -			write_rgb(&rgb24[j * 8 + 4 + rgb24_stride], &rgb[3]);
> -		}
> -
> -		if (cvt->dst.fb->width & 1) {
> -			/* Convert 1x2 pixel block */
> -			struct igt_vec4 yuv[2];
> -			struct igt_vec4 rgb[2];
> -
> -			yuv[0].d[0] = y[j * 2 + 0];
> -			yuv[1].d[0] = y[j * 2 + 0 + planar_stride];
> +	igt_assert(igt_format_is_yuv(fb->drm_format));
>  
> -			yuv[0].d[1] = yuv[1].d[1] = uv[j * 2 + 0];
> -			yuv[0].d[2] = yuv[1].d[2] = uv[j * 2 + 1];
> -			yuv[0].d[3] = yuv[1].d[3] = 1.0f;
> +	switch (fb->drm_format) {
> +	case DRM_FORMAT_NV12:
> +		params->y_inc = 1;
> +		params->uv_inc = 2;
> +		break;
>  
> -			rgb[0] = igt_matrix_transform(&m, &yuv[0]);
> -			rgb[1] = igt_matrix_transform(&m, &yuv[1]);
> -
> -			write_rgb(&rgb24[j * 8 + 0], &rgb[0]);
> -			write_rgb(&rgb24[j * 8 + 0 + rgb24_stride], &rgb[1]);
> -		}
> -
> -		rgb24 += 2 * rgb24_stride;
> -		y += 2 * planar_stride;
> -		uv += planar_stride;
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +		params->y_inc = 2;
> +		params->uv_inc = 4;
> +		break;
> +
> +	case DRM_FORMAT_XYUV8888:
> +		params->y_inc = 4;
> +		params->uv_inc = 4;
> +		break;
>  	}
>  
> -	if (cvt->dst.fb->height & 1) {
> -		/* Convert last row */
> -		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
> -			/* Convert 2x1 pixel blocks */
> -			struct igt_vec4 yuv[2];
> -			struct igt_vec4 rgb[2];
> -
> -			yuv[0].d[0] = y[j * 2 + 0];
> -			yuv[1].d[0] = y[j * 2 + 1];
> -			yuv[0].d[1] = yuv[1].d[1] = uv[j * 2 + 0];
> -			yuv[0].d[2] = yuv[1].d[2] = uv[j * 2 + 1];
> -			yuv[0].d[3] = yuv[1].d[3] = 1.0f;
> +	switch (fb->drm_format) {
> +	case DRM_FORMAT_NV12:
> +		params->y_stride = fb->strides[0];
> +		params->uv_stride = fb->strides[1];
> +		break;
>  
> -			rgb[0] = igt_matrix_transform(&m, &yuv[0]);
> -			rgb[1] = igt_matrix_transform(&m, &yuv[1]);
> +	case DRM_FORMAT_YUYV:
> +	case DRM_FORMAT_YVYU:
> +	case DRM_FORMAT_UYVY:
> +	case DRM_FORMAT_VYUY:
> +	case DRM_FORMAT_XYUV8888:
> +		params->y_stride = fb->strides[0];
> +		params->uv_stride = fb->strides[0];
> +		break;
> +	}
>  
> -			write_rgb(&rgb24[j * 8 + 0], &rgb[0]);
> -			write_rgb(&rgb24[j * 8 + 4], &rgb[0]);
> -		}
> +	switch (fb->drm_format) {
> +	case DRM_FORMAT_NV12:
> +		params->y_offset = fb->offsets[0];
> +		params->u_offset = fb->offsets[1];
> +		params->v_offset = fb->offsets[1] + 1;
> +		break;
>  
> -		if (cvt->dst.fb->width & 1) {
> -			/* Convert single pixel */
> -			struct igt_vec4 yuv;
> -			struct igt_vec4 rgb;
> +	case DRM_FORMAT_YUYV:
> +		params->y_offset = fb->offsets[0];
> +		params->u_offset = fb->offsets[0] + 1;
> +		params->v_offset = fb->offsets[0] + 3;
> +		break;
>  
> -			yuv.d[0] = y[j * 2 + 0];
> -			yuv.d[1] = uv[j * 2 + 0];
> -			yuv.d[2] = uv[j * 2 + 1];
> -			yuv.d[3] = 1.0f;
> +	case DRM_FORMAT_YVYU:
> +		params->y_offset = fb->offsets[0];
> +		params->u_offset = fb->offsets[0] + 3;
> +		params->v_offset = fb->offsets[0] + 1;
> +		break;
>  
> -			rgb = igt_matrix_transform(&m, &yuv);
> +	case DRM_FORMAT_UYVY:
> +		params->y_offset = fb->offsets[0] + 1;
> +		params->u_offset = fb->offsets[0];
> +		params->v_offset = fb->offsets[0] + 2;
> +		break;
>  
> -			write_rgb(&rgb24[j * 8 + 0], &rgb);
> -		}
> +	case DRM_FORMAT_VYUY:
> +		params->y_offset = fb->offsets[0] + 1;
> +		params->u_offset = fb->offsets[0] + 2;
> +		params->v_offset = fb->offsets[0];
> +		break;
> +
> +	case DRM_FORMAT_XYUV8888:
> +		params->y_offset = fb->offsets[0] + 1;
> +		params->u_offset = fb->offsets[0] + 2;
> +		params->v_offset = fb->offsets[0] + 3;
> +		break;
>  	}
> -
> -	convert_src_put(cvt, buf);
>  }
>  
> -static void convert_yuv444_to_rgb24(struct fb_convert *cvt)
> +static void convert_yuv_to_rgb24(struct fb_convert *cvt)
>  {
> +	const struct format_desc_struct *src_fmt =
> +		lookup_drm_format(cvt->src.fb->drm_format);
>  	int i, j;
> -	uint8_t *yuv24;
> +	uint8_t bpp = 4;
> +	uint8_t *y, *u, *v;
>  	uint8_t *rgb24 = cvt->dst.ptr;
> -	unsigned rgb24_stride = cvt->dst.fb->strides[0], xyuv_stride = cvt->src.fb->strides[0];
> -	uint8_t *buf = malloc(cvt->src.fb->size);
> +	unsigned int rgb24_stride = cvt->dst.fb->strides[0];
>  	struct igt_mat4 m = igt_ycbcr_to_rgb_matrix(cvt->src.fb->color_encoding,
>  						    cvt->src.fb->color_range);
> +	uint8_t *buf;
> +	struct yuv_parameters params = { };
>  
> -	/*
> -	 * Reading from the BO is awfully slow because of lack of read caching,
> -	 * it's faster to copy the whole BO to a temporary buffer and convert
> -	 * from there.
> -	 */
> -	igt_memcpy_from_wc(buf, cvt->src.ptr + cvt->src.fb->offsets[0], cvt->src.fb->size);
> -	yuv24 = buf;
> +	igt_assert(cvt->dst.fb->drm_format == DRM_FORMAT_XRGB8888 &&
> +		   igt_format_is_yuv(cvt->src.fb->drm_format));
> +
> +	buf = convert_src_get(cvt);
> +	get_yuv_parameters(cvt->src.fb, &params);
> +	y = buf + params.y_offset;
> +	u = buf + params.u_offset;
> +	v = buf + params.v_offset;
>  
>  	for (i = 0; i < cvt->dst.fb->height; i++) {
> +		const uint8_t *y_tmp = y;
> +		const uint8_t *u_tmp = u;
> +		const uint8_t *v_tmp = v;
> +		uint8_t *rgb_tmp = rgb24;
> +
>  		for (j = 0; j < cvt->dst.fb->width; j++) {
> -			float y, u, v;
> -			struct igt_vec4 yuv;
> -			struct igt_vec4 rgb;
> -
> -			v = yuv24[i * xyuv_stride + j * 4];
> -			u = yuv24[i * xyuv_stride + j * 4 + 1];
> -			y = yuv24[i * xyuv_stride + j * 4 + 2];
> -			yuv.d[0] = y;
> -			yuv.d[1] = u;
> -			yuv.d[2] = v;
> +			struct igt_vec4 rgb, yuv;
> +
> +			yuv.d[0] = *y_tmp;
> +			yuv.d[1] = *u_tmp;
> +			yuv.d[2] = *v_tmp;
>  			yuv.d[3] = 1.0f;
>  
>  			rgb = igt_matrix_transform(&m, &yuv);
> +			write_rgb(rgb_tmp, &rgb);
>  
> -			write_rgb(&rgb24[i * rgb24_stride + j * 4], &rgb);
> -		}
> -	}
> -
> -	free(buf);
> -}
> -
> -
> -static void convert_rgb24_to_yuv444(struct fb_convert *cvt)
> -{
> -	int i, j;
> -	uint8_t *rgb24;
> -	uint8_t *yuv444 = cvt->dst.ptr + cvt->dst.fb->offsets[0];
> -	unsigned int rgb24_stride = cvt->src.fb->strides[0], xyuv_stride = cvt->dst.fb->strides[0];
> -	struct igt_mat4 m = igt_rgb_to_ycbcr_matrix(cvt->dst.fb->color_encoding,
> -						    cvt->dst.fb->color_range);
> -
> -	rgb24 = cvt->src.ptr;
> +			rgb_tmp += bpp;
> +			y_tmp += params.y_inc;
>  
> -	igt_assert_f(cvt->dst.fb->drm_format == DRM_FORMAT_XYUV8888,
> -		     "Conversion not implemented for !XYUV packed formats\n");
> -
> -	for (i = 0; i < cvt->dst.fb->height; i++) {
> -		for (j = 0; j < cvt->dst.fb->width; j++) {
> -			struct igt_vec4 rgb;
> -			struct igt_vec4 yuv;
> -
> -			read_rgb(&rgb, &rgb24[i * rgb24_stride + j * 4]);
> +			if ((src_fmt->hsub == 1) || (j % src_fmt->hsub)) {
> +				u_tmp += params.uv_inc;
> +				v_tmp += params.uv_inc;
> +			}
> +		}
>  
> -			yuv = igt_matrix_transform(&m, &rgb);
> +		rgb24 += rgb24_stride;
> +		y += params.y_stride;
>  
> -			yuv444[i * xyuv_stride + j * 4] = yuv.d[2];
> -			yuv444[i * xyuv_stride + j * 4 + 1] = yuv.d[1];
> -			yuv444[i * xyuv_stride + j * 4 + 2] = yuv.d[0];
> +		if ((src_fmt->vsub == 1) || (i % src_fmt->vsub)) {
> +			u += params.uv_stride;
> +			v += params.uv_stride;
>  		}
>  	}
> +
> +	convert_src_put(cvt, buf);
>  }
>  
> -static void convert_rgb24_to_nv12(struct fb_convert *cvt)
> +static void convert_rgb24_to_yuv(struct fb_convert *cvt)
>  {
> +	const struct format_desc_struct *dst_fmt =
> +		lookup_drm_format(cvt->dst.fb->drm_format);
>  	int i, j;
> -	uint8_t *y = cvt->dst.ptr + cvt->dst.fb->offsets[0];
> -	uint8_t *uv = cvt->dst.ptr + cvt->dst.fb->offsets[1];
> +	uint8_t *y, *u, *v;
>  	const uint8_t *rgb24 = cvt->src.ptr;
> +	uint8_t bpp = 4;
>  	unsigned rgb24_stride = cvt->src.fb->strides[0];
> -	unsigned planar_stride = cvt->dst.fb->strides[0];
>  	struct igt_mat4 m = igt_rgb_to_ycbcr_matrix(cvt->dst.fb->color_encoding,
>  						    cvt->dst.fb->color_range);
> +	struct yuv_parameters params = { };
>  
>  	igt_assert(cvt->src.fb->drm_format == DRM_FORMAT_XRGB8888 &&
> -		   cvt->dst.fb->drm_format == DRM_FORMAT_NV12);
> +		   igt_format_is_yuv(cvt->dst.fb->drm_format));
>  
> -	for (i = 0; i < cvt->dst.fb->height / 2; i++) {
> -		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
> -			/* Convert 2x2 pixel blocks */
> -			struct igt_vec4 rgb[4];
> -			struct igt_vec4 yuv[4];
> +	get_yuv_parameters(cvt->dst.fb, &params);
> +	y = cvt->dst.ptr + params.y_offset;
> +	u = cvt->dst.ptr + params.u_offset;
> +	v = cvt->dst.ptr + params.v_offset;
>  
> -			read_rgb(&rgb[0], &rgb24[j * 8 + 0]);
> -			read_rgb(&rgb[1], &rgb24[j * 8 + 4]);
> -			read_rgb(&rgb[2], &rgb24[j * 8 + 0 + rgb24_stride]);
> -			read_rgb(&rgb[3], &rgb24[j * 8 + 4 + rgb24_stride]);
> -
> -			yuv[0] = igt_matrix_transform(&m, &rgb[0]);
> -			yuv[1] = igt_matrix_transform(&m, &rgb[1]);
> -			yuv[2] = igt_matrix_transform(&m, &rgb[2]);
> -			yuv[3] = igt_matrix_transform(&m, &rgb[3]);
> -
> -			y[j * 2 + 0] = yuv[0].d[0];
> -			y[j * 2 + 1] = yuv[1].d[0];
> -			y[j * 2 + 0 + planar_stride] = yuv[2].d[0];
> -			y[j * 2 + 1 + planar_stride] = yuv[3].d[0];
> +	for (i = 0; i < cvt->dst.fb->height; i++) {
> +		const uint8_t *rgb_tmp = rgb24;
> +		uint8_t *y_tmp = y;
> +		uint8_t *u_tmp = u;
> +		uint8_t *v_tmp = v;
>  
> -			/*
> -			 * We assume the MPEG2 chroma siting convention, where
> -			 * pixel center for Cb'Cr' is between the left top and
> -			 * bottom pixel in a 2x2 block, so take the average.
> -			 */
> -			uv[j * 2 + 0] = (yuv[0].d[1] + yuv[2].d[1]) / 2.0f;
> -			uv[j * 2 + 1] = (yuv[0].d[2] + yuv[2].d[2]) / 2.0f;
> -		}
> +		for (j = 0; j < cvt->dst.fb->width; j++) {
> +			const uint8_t *pair_rgb24 = rgb_tmp;
> +			struct igt_vec4 pair_rgb, rgb;
> +			struct igt_vec4 pair_yuv, yuv;
>  
> -		if (cvt->dst.fb->width & 1) {
> -			/* Convert 1x2 pixel block */
> -			struct igt_vec4 rgb[2];
> -			struct igt_vec4 yuv[2];
> +			read_rgb(&rgb, rgb_tmp);
> +			yuv = igt_matrix_transform(&m, &rgb);
>  
> -			read_rgb(&rgb[0], &rgb24[j * 8 + 0]);
> -			read_rgb(&rgb[2], &rgb24[j * 8 + 0 + rgb24_stride]);
> +			rgb_tmp += bpp;
>  
> -			yuv[0] = igt_matrix_transform(&m, &rgb[0]);
> -			yuv[1] = igt_matrix_transform(&m, &rgb[1]);
> +			*y_tmp = yuv.d[0];
> +			y_tmp += params.y_inc;
>  
> -			y[j * 2 + 0] = yuv[0].d[0];
> -			y[j * 2 + 0 + planar_stride] = yuv[1].d[0];
> +			if ((i % dst_fmt->vsub) || (j % dst_fmt->hsub))
> +				continue;
>  
>  			/*
>  			 * We assume the MPEG2 chroma siting convention, where
>  			 * pixel center for Cb'Cr' is between the left top and
>  			 * bottom pixel in a 2x2 block, so take the average.
> +			 *
> +			 * Therefore, if we use subsampling, we only really care
> +			 * about two pixels all the time, either the two
> +			 * subsequent pixels horizontally, vertically, or the
> +			 * two corners in a 2x2 block.
> +			 *
> +			 * The only corner case is when we have an odd number of
> +			 * pixels, but this can be handled pretty easily by not
> +			 * incrementing the paired pixel pointer in the
> +			 * direction it's odd in.
>  			 */
> -			uv[j * 2 + 0] = (yuv[0].d[1] + yuv[1].d[1]) / 2.0f;
> -			uv[j * 2 + 1] = (yuv[0].d[2] + yuv[1].d[2]) / 2.0f;
> -		}
> -
> -		rgb24 += 2 * rgb24_stride;
> -		y += 2 * planar_stride;
> -		uv += planar_stride;
> -	}
> -
> -	/* Last row cannot be interpolated between 2 pixels, take the single value */
> -	if (cvt->dst.fb->height & 1) {
> -		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
> -			/* Convert 2x1 pixel blocks */
> -			struct igt_vec4 rgb[2];
> -			struct igt_vec4 yuv[2];
> -
> -			read_rgb(&rgb[0], &rgb24[j * 8 + 0]);
> -			read_rgb(&rgb[1], &rgb24[j * 8 + 4]);
> -
> -			yuv[0] = igt_matrix_transform(&m, &rgb[0]);
> -			yuv[1] = igt_matrix_transform(&m, &rgb[1]);
> +			if (j != (cvt->dst.fb->width - 1))
> +				pair_rgb24 += (dst_fmt->hsub - 1) * bpp;
>  
> -			y[j * 2 + 0] = yuv[0].d[0];
> -			y[j * 2 + 1] = yuv[1].d[0];
> -			uv[j * 2 + 0] = yuv[0].d[1];
> -			uv[j * 2 + 1] = yuv[0].d[2];
> -		}
> -
> -		if (cvt->dst.fb->width & 1) {
> -			/* Convert single pixel */
> -			struct igt_vec4 rgb;
> -			struct igt_vec4 yuv;
> +			if (i != (cvt->dst.fb->height - 1))
> +				pair_rgb24 += rgb24_stride * (dst_fmt->vsub - 1);
>  
> -			read_rgb(&rgb, &rgb24[j * 8 + 0]);
> +			read_rgb(&pair_rgb, pair_rgb24);
> +			pair_yuv = igt_matrix_transform(&m, &pair_rgb);
>  
> -			yuv = igt_matrix_transform(&m, &rgb);
> +			*u_tmp = (yuv.d[1] + pair_yuv.d[1]) / 2.0f;
> +			*v_tmp = (yuv.d[2] + pair_yuv.d[2]) / 2.0f;
>  
> -			y[j * 2 + 0] = yuv.d[0];
> -			uv[j * 2 + 0] = yuv.d[1];
> -			uv[j * 2 + 1] = yuv.d[2];
> -		}
> -	}
> -}
> -
> -/* { Y0, U, Y1, V } */
> -static const unsigned char swizzle_yuyv[] = { 0, 1, 2, 3 };
> -static const unsigned char swizzle_yvyu[] = { 0, 3, 2, 1 };
> -static const unsigned char swizzle_uyvy[] = { 1, 0, 3, 2 };
> -static const unsigned char swizzle_vyuy[] = { 1, 2, 3, 0 };
> -
> -static const unsigned char *yuyv_swizzle(uint32_t format)
> -{
> -	switch (format) {
> -	default:
> -	case DRM_FORMAT_YUYV:
> -		return swizzle_yuyv;
> -	case DRM_FORMAT_YVYU:
> -		return swizzle_yvyu;
> -	case DRM_FORMAT_UYVY:
> -		return swizzle_uyvy;
> -	case DRM_FORMAT_VYUY:
> -		return swizzle_vyuy;
> -	}
> -}
> -
> -static void convert_yuyv_to_rgb24(struct fb_convert *cvt)
> -{
> -	int i, j;
> -	const uint8_t *yuyv;
> -	uint8_t *rgb24 = cvt->dst.ptr;
> -	unsigned int rgb24_stride = cvt->dst.fb->strides[0];
> -	unsigned int yuyv_stride = cvt->src.fb->strides[0];
> -	struct igt_mat4 m = igt_ycbcr_to_rgb_matrix(cvt->src.fb->color_encoding,
> -						    cvt->src.fb->color_range);
> -	const unsigned char *swz = yuyv_swizzle(cvt->src.fb->drm_format);
> -	uint8_t *buf;
> -
> -	igt_assert((cvt->src.fb->drm_format == DRM_FORMAT_YUYV ||
> -		    cvt->src.fb->drm_format == DRM_FORMAT_UYVY ||
> -		    cvt->src.fb->drm_format == DRM_FORMAT_YVYU ||
> -		    cvt->src.fb->drm_format == DRM_FORMAT_VYUY) &&
> -		   cvt->dst.fb->drm_format == DRM_FORMAT_XRGB8888);
> -
> -	buf = convert_src_get(cvt);
> -	yuyv = buf;
> -
> -	for (i = 0; i < cvt->dst.fb->height; i++) {
> -		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
> -			/* Convert 2x1 pixel blocks */
> -			struct igt_vec4 yuv[2];
> -			struct igt_vec4 rgb[2];
> -
> -			yuv[0].d[0] = yuyv[j * 4 + swz[0]];
> -			yuv[1].d[0] = yuyv[j * 4 + swz[2]];
> -			yuv[0].d[1] = yuv[1].d[1] = yuyv[j * 4 + swz[1]];
> -			yuv[0].d[2] = yuv[1].d[2] = yuyv[j * 4 + swz[3]];
> -			yuv[0].d[3] = yuv[1].d[3] = 1.0f;
> -
> -			rgb[0] = igt_matrix_transform(&m, &yuv[0]);
> -			rgb[1] = igt_matrix_transform(&m, &yuv[1]);
> -
> -			write_rgb(&rgb24[j * 8 + 0], &rgb[0]);
> -			write_rgb(&rgb24[j * 8 + 4], &rgb[1]);
> -		}
> -
> -		if (cvt->dst.fb->width & 1) {
> -			struct igt_vec4 yuv;
> -			struct igt_vec4 rgb;
> -
> -			yuv.d[0] = yuyv[j * 4 + swz[0]];
> -			yuv.d[1] = yuyv[j * 4 + swz[1]];
> -			yuv.d[2] = yuyv[j * 4 + swz[3]];
> -			yuv.d[3] = 1.0f;
> -
> -			rgb = igt_matrix_transform(&m, &yuv);
> -
> -			write_rgb(&rgb24[j * 8 + 0], &rgb);
> +			u_tmp += params.uv_inc;
> +			v_tmp += params.uv_inc;
>  		}
>  
>  		rgb24 += rgb24_stride;
> -		yuyv += yuyv_stride;
> -	}
> -
> -	convert_src_put(cvt, buf);
> -}
> -
> -static void convert_rgb24_to_yuyv(struct fb_convert *cvt)
> -{
> -	int i, j;
> -	uint8_t *yuyv = cvt->dst.ptr;
> -	const uint8_t *rgb24 = cvt->src.ptr;
> -	unsigned rgb24_stride = cvt->src.fb->strides[0];
> -	unsigned yuyv_stride = cvt->dst.fb->strides[0];
> -	struct igt_mat4 m = igt_rgb_to_ycbcr_matrix(cvt->dst.fb->color_encoding,
> -						    cvt->dst.fb->color_range);
> -	const unsigned char *swz = yuyv_swizzle(cvt->dst.fb->drm_format);
> -
> -	igt_assert(cvt->src.fb->drm_format == DRM_FORMAT_XRGB8888 &&
> -		   (cvt->dst.fb->drm_format == DRM_FORMAT_YUYV ||
> -		    cvt->dst.fb->drm_format == DRM_FORMAT_UYVY ||
> -		    cvt->dst.fb->drm_format == DRM_FORMAT_YVYU ||
> -		    cvt->dst.fb->drm_format == DRM_FORMAT_VYUY));
> -
> -	for (i = 0; i < cvt->dst.fb->height; i++) {
> -		for (j = 0; j < cvt->dst.fb->width / 2; j++) {
> -			/* Convert 2x1 pixel blocks */
> -			struct igt_vec4 rgb[2];
> -			struct igt_vec4 yuv[2];
> -
> -			read_rgb(&rgb[0], &rgb24[j * 8 + 0]);
> -			read_rgb(&rgb[1], &rgb24[j * 8 + 4]);
> -
> -			yuv[0] = igt_matrix_transform(&m, &rgb[0]);
> -			yuv[1] = igt_matrix_transform(&m, &rgb[1]);
> +		y += params.y_stride;
>  
> -			yuyv[j * 4 + swz[0]] = yuv[0].d[0];
> -			yuyv[j * 4 + swz[2]] = yuv[1].d[0];
> -			yuyv[j * 4 + swz[1]] = (yuv[0].d[1] + yuv[1].d[1]) / 2.0f;
> -			yuyv[j * 4 + swz[3]] = (yuv[0].d[2] + yuv[1].d[2]) / 2.0f;
> +		if ((i % dst_fmt->vsub) == (dst_fmt->vsub - 1)) {
> +			u += params.uv_stride;
> +			v += params.uv_stride;
>  		}
> -
> -		if (cvt->dst.fb->width & 1) {
> -			struct igt_vec4 rgb;
> -			struct igt_vec4 yuv;
> -
> -			read_rgb(&rgb, &rgb24[j * 8 + 0]);
> -
> -			yuv = igt_matrix_transform(&m, &rgb);
> -
> -			yuyv[j * 4 + swz[0]] = yuv.d[0];
> -			yuyv[j * 4 + swz[1]] = yuv.d[1];
> -			yuyv[j * 4 + swz[3]] = yuv.d[2];
> -		}
> -
> -		rgb24 += rgb24_stride;
> -		yuyv += yuyv_stride;
>  	}
>  }
>  
> @@ -2046,31 +1859,23 @@ static void fb_convert(struct fb_convert *cvt)
>  	} else if (cvt->dst.fb->drm_format == DRM_FORMAT_XRGB8888) {
>  		switch (cvt->src.fb->drm_format) {
>  		case DRM_FORMAT_XYUV8888:
> -			convert_yuv444_to_rgb24(cvt);
> -			return;
>  		case DRM_FORMAT_NV12:
> -			convert_nv12_to_rgb24(cvt);
> -			return;
> -		case DRM_FORMAT_YUYV:
> -		case DRM_FORMAT_YVYU:
>  		case DRM_FORMAT_UYVY:
>  		case DRM_FORMAT_VYUY:
> -			convert_yuyv_to_rgb24(cvt);
> +		case DRM_FORMAT_YUYV:
> +		case DRM_FORMAT_YVYU:
> +			convert_yuv_to_rgb24(cvt);
>  			return;
>  		}
>  	} else if (cvt->src.fb->drm_format == DRM_FORMAT_XRGB8888) {
>  		switch (cvt->dst.fb->drm_format) {
>  		case DRM_FORMAT_XYUV8888:
> -			convert_rgb24_to_yuv444(cvt);
> -			return;
>  		case DRM_FORMAT_NV12:
> -			convert_rgb24_to_nv12(cvt);
> -			return;
>  		case DRM_FORMAT_YUYV:
>  		case DRM_FORMAT_YVYU:
>  		case DRM_FORMAT_UYVY:
>  		case DRM_FORMAT_VYUY:
> -			convert_rgb24_to_yuyv(cvt);
> +			convert_rgb24_to_yuv(cvt);
>  			return;
>  		}
>  	}