Le 13/08/2025 à 19:05, Robert Mader a écrit :
Analogous to the existing pixel_read_line_t and vkms_plane_state
implementation.
Note that the chroma siting is implecitely set ITU-T Rec.H.273
Chroma420SampleLocType 2, which should match the existing plane code
and the corresponding Mesa shader paths.
Signed-off-by: Robert Mader <[email protected]>
---
drivers/gpu/drm/vkms/tests/vkms_format_test.c | 51 ++++--
drivers/gpu/drm/vkms/vkms_drv.h | 36 +++-
drivers/gpu/drm/vkms/vkms_formats.c | 172 ++++++++++++++++--
drivers/gpu/drm/vkms/vkms_formats.h | 3 +
4 files changed, 225 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/drm/vkms/tests/vkms_format_test.c
b/drivers/gpu/drm/vkms/tests/vkms_format_test.c
index a7788fbc45dc..68b070160dca 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_format_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_format_test.c
@@ -13,19 +13,6 @@
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
-/**
- * struct pixel_yuv_u16 - Internal representation of a pixel color.
- * @y: Luma value, stored in 16 bits, without padding, using
- * machine endianness
- * @u: Blue difference chroma value, stored in 16 bits, without padding, using
- * machine endianness
- * @v: Red difference chroma value, stored in 16 bits, without padding, using
- * machine endianness
- */
-struct pixel_yuv_u16 {
- u16 y, u, v;
-};
-
/*
* struct yuv_u16_to_argb_u16_case - Reference values to test the color
* conversions in VKMS between YUV to ARGB
@@ -252,6 +239,43 @@ static void vkms_format_test_yuv_u16_to_argb_u16(struct
kunit *test)
}
}
+/*
+ * vkms_format_test_yuv_u16_to_argb_u16 - Testing the conversion between ARGB
+ * colors to YUV colors in VKMS
+ *
+ * This test will use the functions get_conversion_matrix_from_argb_u16 and
+ * yuv161616_from_argb_u16 to convert ARGB colors (stored in
+ * yuv_u16_to_argb_u16_cases) into YUV colors.
+ *
+ * The conversion between YUV and RGB is not totally reversible, so there may
be
+ * some difference between the expected value and the result.
+ */
+static void vkms_format_test_argb_u16_to_yuv_u16(struct kunit *test)
+{
+ const struct yuv_u16_to_argb_u16_case *param = test->param_value;
+ struct pixel_yuv_u16 yuv;
+
+ for (size_t i = 0; i < param->n_colors; i++) {
+ const struct format_pair *color = ¶m->colors[i];
+ struct conversion_matrix matrix;
+
+ get_conversion_matrix_from_argb_u16
+ (DRM_FORMAT_NV12, param->encoding, param->range,
&matrix);
+
+ yuv = yuv161616_from_argb_u16(&matrix, &color->argb);
+
+ KUNIT_EXPECT_LE_MSG(test, abs_diff(color->yuv.y, yuv.y), 0x1ff,
+ "On the Y channel of the color %s expected
0x%04x, got 0x%04x",
+ color->name, color->yuv.y, yuv.y);
+ KUNIT_EXPECT_LE_MSG(test, abs_diff(color->yuv.u, yuv.u), 0x1ff,
+ "On the U channel of the color %s expected
0x%04x, got 0x%04x",
+ color->name, color->yuv.u, yuv.u);
+ KUNIT_EXPECT_LE_MSG(test, abs_diff(color->yuv.v, yuv.v), 0x1ff,
+ "On the V channel of the color %s expected
0x%04x, got 0x%04x",
+ color->name, color->yuv.v, yuv.v);
+ }
+}
+
static void vkms_format_test_yuv_u16_to_argb_u16_case_desc(struct
yuv_u16_to_argb_u16_case *t,
char *desc)
{
@@ -265,6 +289,7 @@ KUNIT_ARRAY_PARAM(yuv_u16_to_argb_u16,
yuv_u16_to_argb_u16_cases,
static struct kunit_case vkms_format_test_cases[] = {
KUNIT_CASE_PARAM(vkms_format_test_yuv_u16_to_argb_u16,
yuv_u16_to_argb_u16_gen_params),
+ KUNIT_CASE_PARAM(vkms_format_test_argb_u16_to_yuv_u16,
yuv_u16_to_argb_u16_gen_params),
{}
};
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 7fa58e17c286..29dddc973ef6 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -66,21 +66,24 @@ struct pixel_argb_u16 {
u16 a, r, g, b;
};
+/**
+ * struct pixel_yuv_u16 - Internal representation of a pixel color.
+ * @y: Luma value, stored in 16 bits, without padding, using
+ * machine endianness
+ * @u: Blue difference chroma value, stored in 16 bits, without padding, using
+ * machine endianness
+ * @v: Red difference chroma value, stored in 16 bits, without padding, using
+ * machine endianness
+ */
+struct pixel_yuv_u16 {
+ u16 y, u, v;
+};
+
struct line_buffer {
size_t n_pixels;
struct pixel_argb_u16 *pixels;
};
-/**
- * typedef pixel_write_t - These functions are used to read a pixel from a
- * &struct pixel_argb_u16, convert it in a specific format and write it in the
@out_pixel
- * buffer.
- *
- * @out_pixel: destination address to write the pixel
- * @in_pixel: pixel to write
- */
-typedef void (*pixel_write_t)(u8 *out_pixel, const struct pixel_argb_u16
*in_pixel);
-
/**
* struct conversion_matrix - Matrix to use for a specific encoding and range
*
@@ -97,6 +100,19 @@ struct conversion_matrix {
int y_offset;
};
+/**
+ * typedef pixel_write_t - These functions are used to read a pixel from a
+ * &struct pixel_argb_u16, convert it in a specific format and write it in the
@out_pixel
+ * buffer.
+ *
Can you document the new parameters?
+ * @out_pixel: destination address to write the pixel
+ * @in_pixel: pixel to write
+ */
+typedef void (*pixel_write_t)(const struct conversion_matrix *matrix,
+ u8 *out_pixel, u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16 *in_pixel);
+
struct vkms_writeback_job {
struct iosys_map data[DRM_FORMAT_MAX_PLANES];
struct vkms_frame_info wb_frame_info;
diff --git a/drivers/gpu/drm/vkms/vkms_formats.c
b/drivers/gpu/drm/vkms/vkms_formats.c
index 560b56fbf4fb..048268304c27 100644
--- a/drivers/gpu/drm/vkms/vkms_formats.c
+++ b/drivers/gpu/drm/vkms/vkms_formats.c
@@ -585,7 +585,9 @@ static void planar_yuv_read_line(const struct
vkms_plane_state *plane, int x_sta
* They are used in vkms_writeback_row() to convert and store a pixel from
the src_buffer to
* the writeback buffer.
*/
-static void argb_u16_to_ARGB8888(u8 *out_pixel, const struct pixel_argb_u16
*in_pixel)
+static void argb_u16_to_ARGB8888(const struct conversion_matrix *matrix,
+ u8 *out_pixel, u8 *out_pixel_plane_2, u8
*out_pixel_plane_3,
+ const struct pixel_argb_u16 *in_pixel)
{
/*
* This sequence below is important because the format's byte order is
@@ -603,7 +605,10 @@ static void argb_u16_to_ARGB8888(u8 *out_pixel, const
struct pixel_argb_u16 *in_
out_pixel[0] = DIV_ROUND_CLOSEST(in_pixel->b, 257);
}
-static void argb_u16_to_XRGB8888(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
+static void argb_u16_to_XRGB8888(const struct conversion_matrix *matrix,
+ u8 *out_pixel, u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16 *in_pixel)
{
out_pixel[3] = 0xff;
out_pixel[2] = DIV_ROUND_CLOSEST(in_pixel->r, 257);
@@ -611,7 +616,10 @@ static void argb_u16_to_XRGB8888(u8 *out_pixel, const
struct pixel_argb_u16 *in_
out_pixel[0] = DIV_ROUND_CLOSEST(in_pixel->b, 257);
}
-static void argb_u16_to_ABGR8888(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
+static void argb_u16_to_ABGR8888(const struct conversion_matrix *matrix,
+ u8 *out_pixel, u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16 *in_pixel)
{
out_pixel[3] = DIV_ROUND_CLOSEST(in_pixel->a, 257);
out_pixel[2] = DIV_ROUND_CLOSEST(in_pixel->b, 257);
@@ -619,7 +627,10 @@ static void argb_u16_to_ABGR8888(u8 *out_pixel, const
struct pixel_argb_u16 *in_
out_pixel[0] = DIV_ROUND_CLOSEST(in_pixel->r, 257);
}
-static void argb_u16_to_ARGB16161616(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
+static void argb_u16_to_ARGB16161616(const struct conversion_matrix *matrix,
+ u8 *out_pixel, u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16 *in_pixel)
{
__le16 *pixel = (__le16 *)out_pixel;
@@ -629,7 +640,10 @@ static void argb_u16_to_ARGB16161616(u8 *out_pixel, const struct pixel_argb_u16
pixel[0] = cpu_to_le16(in_pixel->b);
}
-static void argb_u16_to_XRGB16161616(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
+static void argb_u16_to_XRGB16161616(const struct conversion_matrix *matrix,
+ u8 *out_pixel, u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16 *in_pixel)
{
__le16 *pixel = (__le16 *)out_pixel;
@@ -639,7 +653,10 @@ static void argb_u16_to_XRGB16161616(u8 *out_pixel, const struct pixel_argb_u16
pixel[0] = cpu_to_le16(in_pixel->b);
}
-static void argb_u16_to_RGB565(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
+static void argb_u16_to_RGB565(const struct conversion_matrix *matrix,
+ u8 *out_pixel, u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16 *in_pixel)
{
__le16 *pixel = (__le16 *)out_pixel;
@@ -657,6 +674,96 @@ static void argb_u16_to_RGB565(u8 *out_pixel, const struct pixel_argb_u16 *in_pi
*pixel = cpu_to_le16(r << 11 | g << 5 | b);
}
+VISIBLE_IF_KUNIT
+struct pixel_yuv_u16 yuv161616_from_argb_u16(const struct conversion_matrix
*matrix,
+ const struct pixel_argb_u16
*in_pixel)
+{
+ struct pixel_yuv_u16 out_pixel;
+ s64 fp_r, fp_g, fp_b;
+ s64 fp_y, fp_channel_1, fp_channel_2;
+
+ fp_r = drm_int2fixp((int)in_pixel->r * 257);
+ fp_g = drm_int2fixp((int)in_pixel->g * 257);
+ fp_b = drm_int2fixp((int)in_pixel->b * 257);
+
+ fp_y = drm_fixp_mul(matrix->matrix[0][0], fp_r) +
+ drm_fixp_mul(matrix->matrix[0][1], fp_g) +
+ drm_fixp_mul(matrix->matrix[0][2], fp_b);
+ fp_channel_1 = drm_fixp_mul(matrix->matrix[1][0], fp_r) +
+ drm_fixp_mul(matrix->matrix[1][1], fp_g) +
+ drm_fixp_mul(matrix->matrix[1][2], fp_b);
+ fp_channel_2 = drm_fixp_mul(matrix->matrix[2][0], fp_r) +
+ drm_fixp_mul(matrix->matrix[2][1], fp_g) +
+ drm_fixp_mul(matrix->matrix[2][2], fp_b);
+
+ fp_y = drm_fixp2int_round(fp_y);
+ fp_channel_1 = drm_fixp2int_round(fp_channel_1);
+ fp_channel_2 = drm_fixp2int_round(fp_channel_2);
+
+ fp_y = DIV_ROUND_CLOSEST(fp_y, 257);
+ fp_channel_1 = DIV_ROUND_CLOSEST(fp_channel_1, 257);
+ fp_channel_2 = DIV_ROUND_CLOSEST(fp_channel_2, 257);
+
+ fp_y += matrix->y_offset * 257;
+ fp_channel_1 += 128 * 257;
+ fp_channel_2 += 128 * 257;
+
+ out_pixel.y = clamp(fp_y, 0, 0xffff);
+ out_pixel.u = clamp(fp_channel_1, 0, 0xffff);
+ out_pixel.v = clamp(fp_channel_2, 0, 0xffff);
+
+ return out_pixel;
+}
+EXPORT_SYMBOL_IF_KUNIT(yuv161616_from_argb_u16);
+
+static void argb_u16_to_YUV888_semiplanar_2plane(const struct
conversion_matrix *matrix,
+ u8 *out_pixel,
+ u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16
*in_pixel)
+{
+ struct pixel_yuv_u16 yuv;
+
+ yuv = yuv161616_from_argb_u16(matrix, in_pixel);
+
+ out_pixel[0] = DIV_ROUND_CLOSEST(yuv.y, 257);
+ out_pixel_plane_2[0] = DIV_ROUND_CLOSEST(yuv.u, 257);
+ out_pixel_plane_2[1] = DIV_ROUND_CLOSEST(yuv.v, 257);
+}
+
+static void argb_u16_to_YUV161616_semiplanar_2plane(const struct
conversion_matrix *matrix,
+ u8 *out_pixel,
+ u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16
*in_pixel)
+{
+ struct pixel_yuv_u16 yuv;
+
+ yuv = yuv161616_from_argb_u16(matrix, in_pixel);
+
+ out_pixel[0] = yuv.y & 0xff;
+ out_pixel[1] = yuv.y >> 8;
+ out_pixel_plane_2[0] = yuv.u & 0xff;
+ out_pixel_plane_2[1] = yuv.u >> 8;
+ out_pixel_plane_2[2] = yuv.v & 0xff;
+ out_pixel_plane_2[3] = yuv.v >> 8;
+}
+
+static void argb_u16_to_YUV888_semiplanar_3plane(const struct
conversion_matrix *matrix,
+ u8 *out_pixel,
+ u8 *out_pixel_plane_2,
+ u8 *out_pixel_plane_3,
+ const struct pixel_argb_u16
*in_pixel)
+{
+ struct pixel_yuv_u16 yuv;
+
+ yuv = yuv161616_from_argb_u16(matrix, in_pixel);
+
+ out_pixel[0] = DIV_ROUND_CLOSEST(yuv.y, 257);
+ out_pixel_plane_2[0] = DIV_ROUND_CLOSEST(yuv.u, 257);
+ out_pixel_plane_3[0] = DIV_ROUND_CLOSEST(yuv.v, 257);
+}
+
/**
* vkms_writeback_row() - Generic loop for all supported writeback format. It
is executed just
* after the blending to write a line in the writeback buffer.
@@ -669,16 +776,35 @@ void vkms_writeback_row(struct vkms_writeback_job *wb,
const struct line_buffer *src_buffer, int y)
{
struct vkms_frame_info *frame_info = &wb->wb_frame_info;
- int x_dst = frame_info->dst.x1;
- u8 *dst_pixels;
- int rem_x, rem_y;
-
- packed_pixels_addr(frame_info, x_dst, y, 0, &dst_pixels, &rem_x,
&rem_y);
+ const struct drm_format_info *format = frame_info->fb->format;
struct pixel_argb_u16 *in_pixels = src_buffer->pixels;
- int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
src_buffer->n_pixels);
+ int x_limit;
+
+ x_limit= min_t(size_t, drm_rect_width(&frame_info->dst),
+ src_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++) {
+ u8 *plane_1;
+ u8 *plane_2 = NULL;
+ u8 *plane_3 = NULL;
+
+ packed_pixels_addr_1x1(frame_info, x, y, 0, &plane_1);
+ if (format->num_planes > 1) {
+ packed_pixels_addr_1x1(frame_info,
+ x / frame_info->fb->format->hsub,
+ y /
frame_info->fb->format->vsub, 1,
+ &plane_2);
+ }
+ if (format->num_planes > 2) {
+ packed_pixels_addr_1x1(frame_info,
+ x / frame_info->fb->format->hsub,
+ y /
frame_info->fb->format->vsub, 2,
+ &plane_3);
+ }
To avoid performance issue, can you compute the base address of
plane1/plane2/plane3 before the loop and only update the offset inside
the loop, like planar_yuv_read_line.
- for (size_t x = 0; x < x_limit; x++, dst_pixels += frame_info->fb->format->cpp[0])
- wb->pixel_write(dst_pixels, &in_pixels[x]);
+ wb->pixel_write(&wb->conversion_matrix, plane_1, plane_2,
+ plane_3, &in_pixels[x]);
+ }
}
/**
@@ -1110,6 +1236,24 @@ pixel_write_t get_pixel_write_function(u32 format)
return &argb_u16_to_XRGB16161616;
case DRM_FORMAT_RGB565:
return &argb_u16_to_RGB565;
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV24:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV61:
+ case DRM_FORMAT_NV42:
+ return &argb_u16_to_YUV888_semiplanar_2plane;
+ case DRM_FORMAT_P010:
+ case DRM_FORMAT_P012:
+ case DRM_FORMAT_P016:
+ return &argb_u16_to_YUV161616_semiplanar_2plane;
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YUV444:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YVU444:
+ return &argb_u16_to_YUV888_semiplanar_3plane;
default:
/*
* This is a bug in vkms_writeback_atomic_check. All the
supported
diff --git a/drivers/gpu/drm/vkms/vkms_formats.h
b/drivers/gpu/drm/vkms/vkms_formats.h
index 9367672b6b43..bf317c3c058a 100644
--- a/drivers/gpu/drm/vkms/vkms_formats.h
+++ b/drivers/gpu/drm/vkms/vkms_formats.h
@@ -20,6 +20,9 @@ void get_conversion_matrix_from_argb_u16(u32 format, enum
drm_color_encoding enc
#if IS_ENABLED(CONFIG_KUNIT)
struct pixel_argb_u16 argb_u16_from_yuv161616(const struct conversion_matrix
*matrix,
u16 y, u16 channel_1, u16
channel_2);
+
+struct pixel_yuv_u16 yuv161616_from_argb_u16(const struct conversion_matrix
*matrix,
+ const struct pixel_argb_u16
*in_pixel);
#endif
#endif /* _VKMS_FORMATS_H_ */
--
--
Louis Chauvet, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com