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 <robert.ma...@collabora.com> --- 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. + * + * @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); + } - 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_ */ -- 2.50.1