|
|
|
@@ -140,6 +140,51 @@ static void packed_pixels_addr_1x1(const struct vkms_frame_info *frame_info,
|
|
|
|
|
*addr = (u8 *)frame_info->map[0].vaddr + offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* get_subsampling() - Get the subsampling divisor value on a specific direction
|
|
|
|
|
*
|
|
|
|
|
* @format: format to extarct the subsampling from
|
|
|
|
|
* @direction: direction of the subsampling requested
|
|
|
|
|
*/
|
|
|
|
|
static int get_subsampling(const struct drm_format_info *format,
|
|
|
|
|
enum pixel_read_direction direction)
|
|
|
|
|
{
|
|
|
|
|
switch (direction) {
|
|
|
|
|
case READ_BOTTOM_TO_TOP:
|
|
|
|
|
case READ_TOP_TO_BOTTOM:
|
|
|
|
|
return format->vsub;
|
|
|
|
|
case READ_RIGHT_TO_LEFT:
|
|
|
|
|
case READ_LEFT_TO_RIGHT:
|
|
|
|
|
return format->hsub;
|
|
|
|
|
}
|
|
|
|
|
WARN_ONCE(true, "Invalid direction for pixel reading: %d\n", direction);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* get_subsampling_offset() - An offset for keeping the chroma siting consistent regardless of
|
|
|
|
|
* x_start and y_start values
|
|
|
|
|
*
|
|
|
|
|
* @direction: direction of the reading to properly compute this offset
|
|
|
|
|
* @x_start: x coordinate of the starting point of the readed line
|
|
|
|
|
* @y_start: y coordinate of the starting point of the readed line
|
|
|
|
|
*/
|
|
|
|
|
static int get_subsampling_offset(enum pixel_read_direction direction, int x_start, int y_start)
|
|
|
|
|
{
|
|
|
|
|
switch (direction) {
|
|
|
|
|
case READ_BOTTOM_TO_TOP:
|
|
|
|
|
return -y_start - 1;
|
|
|
|
|
case READ_TOP_TO_BOTTOM:
|
|
|
|
|
return y_start;
|
|
|
|
|
case READ_RIGHT_TO_LEFT:
|
|
|
|
|
return -x_start - 1;
|
|
|
|
|
case READ_LEFT_TO_RIGHT:
|
|
|
|
|
return x_start;
|
|
|
|
|
}
|
|
|
|
|
WARN_ONCE(true, "Invalid direction for pixel reading: %d\n", direction);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The following functions take pixel data (a, r, g, b, pixel, ...) and convert them to
|
|
|
|
|
* &struct pixel_argb_u16
|
|
|
|
@@ -202,6 +247,38 @@ static struct pixel_argb_u16 argb_u16_from_RGB565(const __le16 *pixel)
|
|
|
|
|
return out_pixel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct pixel_argb_u16 argb_u16_from_yuv888(u8 y, u8 channel_1, u8 channel_2,
|
|
|
|
|
const struct conversion_matrix *matrix)
|
|
|
|
|
{
|
|
|
|
|
u16 r, g, b;
|
|
|
|
|
s64 fp_y, fp_channel_1, fp_channel_2;
|
|
|
|
|
s64 fp_r, fp_g, fp_b;
|
|
|
|
|
|
|
|
|
|
fp_y = drm_int2fixp(((int)y - matrix->y_offset) * 257);
|
|
|
|
|
fp_channel_1 = drm_int2fixp(((int)channel_1 - 128) * 257);
|
|
|
|
|
fp_channel_2 = drm_int2fixp(((int)channel_2 - 128) * 257);
|
|
|
|
|
|
|
|
|
|
fp_r = drm_fixp_mul(matrix->matrix[0][0], fp_y) +
|
|
|
|
|
drm_fixp_mul(matrix->matrix[0][1], fp_channel_1) +
|
|
|
|
|
drm_fixp_mul(matrix->matrix[0][2], fp_channel_2);
|
|
|
|
|
fp_g = drm_fixp_mul(matrix->matrix[1][0], fp_y) +
|
|
|
|
|
drm_fixp_mul(matrix->matrix[1][1], fp_channel_1) +
|
|
|
|
|
drm_fixp_mul(matrix->matrix[1][2], fp_channel_2);
|
|
|
|
|
fp_b = drm_fixp_mul(matrix->matrix[2][0], fp_y) +
|
|
|
|
|
drm_fixp_mul(matrix->matrix[2][1], fp_channel_1) +
|
|
|
|
|
drm_fixp_mul(matrix->matrix[2][2], fp_channel_2);
|
|
|
|
|
|
|
|
|
|
fp_r = drm_fixp2int_round(fp_r);
|
|
|
|
|
fp_g = drm_fixp2int_round(fp_g);
|
|
|
|
|
fp_b = drm_fixp2int_round(fp_b);
|
|
|
|
|
|
|
|
|
|
r = clamp(fp_r, 0, 0xffff);
|
|
|
|
|
g = clamp(fp_g, 0, 0xffff);
|
|
|
|
|
b = clamp(fp_b, 0, 0xffff);
|
|
|
|
|
|
|
|
|
|
return argb_u16_from_u16161616(0xffff, r, g, b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The following functions are read_line function for each pixel format supported by VKMS.
|
|
|
|
|
*
|
|
|
|
@@ -331,6 +408,92 @@ static void RGB565_read_line(const struct vkms_plane_state *plane, int x_start,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This callback can be used for YUV formats where U and V values are
|
|
|
|
|
* stored in the same plane (often called semi-planar formats). It will
|
|
|
|
|
* correctly handle subsampling as described in the drm_format_info of the plane.
|
|
|
|
|
*
|
|
|
|
|
* The conversion matrix stored in the @plane is used to:
|
|
|
|
|
* - Apply the correct color range and encoding
|
|
|
|
|
* - Convert YUV and YVU with the same function (a column swap is needed when setting up
|
|
|
|
|
* plane->conversion_matrix)
|
|
|
|
|
*/
|
|
|
|
|
static void semi_planar_yuv_read_line(const struct vkms_plane_state *plane, int x_start,
|
|
|
|
|
int y_start, enum pixel_read_direction direction, int count,
|
|
|
|
|
struct pixel_argb_u16 out_pixel[])
|
|
|
|
|
{
|
|
|
|
|
u8 *y_plane;
|
|
|
|
|
u8 *uv_plane;
|
|
|
|
|
|
|
|
|
|
packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0,
|
|
|
|
|
&y_plane);
|
|
|
|
|
packed_pixels_addr_1x1(plane->frame_info,
|
|
|
|
|
x_start / plane->frame_info->fb->format->hsub,
|
|
|
|
|
y_start / plane->frame_info->fb->format->vsub, 1,
|
|
|
|
|
&uv_plane);
|
|
|
|
|
int step_y = get_block_step_bytes(plane->frame_info->fb, direction, 0);
|
|
|
|
|
int step_uv = get_block_step_bytes(plane->frame_info->fb, direction, 1);
|
|
|
|
|
int subsampling = get_subsampling(plane->frame_info->fb->format, direction);
|
|
|
|
|
int subsampling_offset = get_subsampling_offset(direction, x_start, y_start);
|
|
|
|
|
const struct conversion_matrix *conversion_matrix = &plane->conversion_matrix;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
*out_pixel = argb_u16_from_yuv888(y_plane[0], uv_plane[0], uv_plane[1],
|
|
|
|
|
conversion_matrix);
|
|
|
|
|
out_pixel += 1;
|
|
|
|
|
y_plane += step_y;
|
|
|
|
|
if ((i + subsampling_offset + 1) % subsampling == 0)
|
|
|
|
|
uv_plane += step_uv;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This callback can be used for YUV format where each color component is
|
|
|
|
|
* stored in a different plane (often called planar formats). It will
|
|
|
|
|
* correctly handle subsampling as described in the drm_format_info of the plane.
|
|
|
|
|
*
|
|
|
|
|
* The conversion matrix stored in the @plane is used to:
|
|
|
|
|
* - Apply the correct color range and encoding
|
|
|
|
|
* - Convert YUV and YVU with the same function (a column swap is needed when setting up
|
|
|
|
|
* plane->conversion_matrix)
|
|
|
|
|
*/
|
|
|
|
|
static void planar_yuv_read_line(const struct vkms_plane_state *plane, int x_start,
|
|
|
|
|
int y_start, enum pixel_read_direction direction, int count,
|
|
|
|
|
struct pixel_argb_u16 out_pixel[])
|
|
|
|
|
{
|
|
|
|
|
u8 *y_plane;
|
|
|
|
|
u8 *channel_1_plane;
|
|
|
|
|
u8 *channel_2_plane;
|
|
|
|
|
|
|
|
|
|
packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0,
|
|
|
|
|
&y_plane);
|
|
|
|
|
packed_pixels_addr_1x1(plane->frame_info,
|
|
|
|
|
x_start / plane->frame_info->fb->format->hsub,
|
|
|
|
|
y_start / plane->frame_info->fb->format->vsub, 1,
|
|
|
|
|
&channel_1_plane);
|
|
|
|
|
packed_pixels_addr_1x1(plane->frame_info,
|
|
|
|
|
x_start / plane->frame_info->fb->format->hsub,
|
|
|
|
|
y_start / plane->frame_info->fb->format->vsub, 2,
|
|
|
|
|
&channel_2_plane);
|
|
|
|
|
int step_y = get_block_step_bytes(plane->frame_info->fb, direction, 0);
|
|
|
|
|
int step_channel_1 = get_block_step_bytes(plane->frame_info->fb, direction, 1);
|
|
|
|
|
int step_channel_2 = get_block_step_bytes(plane->frame_info->fb, direction, 2);
|
|
|
|
|
int subsampling = get_subsampling(plane->frame_info->fb->format, direction);
|
|
|
|
|
int subsampling_offset = get_subsampling_offset(direction, x_start, y_start);
|
|
|
|
|
const struct conversion_matrix *conversion_matrix = &plane->conversion_matrix;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
*out_pixel = argb_u16_from_yuv888(*y_plane, *channel_1_plane, *channel_2_plane,
|
|
|
|
|
conversion_matrix);
|
|
|
|
|
out_pixel += 1;
|
|
|
|
|
y_plane += step_y;
|
|
|
|
|
if ((i + subsampling_offset + 1) % subsampling == 0) {
|
|
|
|
|
channel_1_plane += step_channel_1;
|
|
|
|
|
channel_2_plane += step_channel_2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The following functions take one &struct pixel_argb_u16 and convert it to a specific format.
|
|
|
|
|
* The result is stored in @out_pixel.
|
|
|
|
@@ -456,6 +619,20 @@ pixel_read_line_t get_pixel_read_line_function(u32 format)
|
|
|
|
|
return &XRGB16161616_read_line;
|
|
|
|
|
case DRM_FORMAT_RGB565:
|
|
|
|
|
return &RGB565_read_line;
|
|
|
|
|
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 &semi_planar_yuv_read_line;
|
|
|
|
|
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 &planar_yuv_read_line;
|
|
|
|
|
default:
|
|
|
|
|
/*
|
|
|
|
|
* This is a bug in vkms_plane_atomic_check(). All the supported
|
|
|
|
@@ -469,6 +646,183 @@ pixel_read_line_t get_pixel_read_line_function(u32 format)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Those matrices were generated using the colour python framework
|
|
|
|
|
*
|
|
|
|
|
* Below are the function calls used to generate each matrix, go to
|
|
|
|
|
* https://colour.readthedocs.io/en/develop/generated/colour.matrix_YCbCr.html
|
|
|
|
|
* for more info:
|
|
|
|
|
*
|
|
|
|
|
* numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.601"],
|
|
|
|
|
* is_legal = False,
|
|
|
|
|
* bits = 8) * 2**32).astype(int)
|
|
|
|
|
*/
|
|
|
|
|
static const struct conversion_matrix no_operation = {
|
|
|
|
|
.matrix = {
|
|
|
|
|
{ 4294967296, 0, 0, },
|
|
|
|
|
{ 0, 4294967296, 0, },
|
|
|
|
|
{ 0, 0, 4294967296, },
|
|
|
|
|
},
|
|
|
|
|
.y_offset = 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct conversion_matrix yuv_bt601_full = {
|
|
|
|
|
.matrix = {
|
|
|
|
|
{ 4294967296, 0, 6021544149 },
|
|
|
|
|
{ 4294967296, -1478054095, -3067191994 },
|
|
|
|
|
{ 4294967296, 7610682049, 0 },
|
|
|
|
|
},
|
|
|
|
|
.y_offset = 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.601"],
|
|
|
|
|
* is_legal = True,
|
|
|
|
|
* bits = 8) * 2**32).astype(int)
|
|
|
|
|
*/
|
|
|
|
|
static const struct conversion_matrix yuv_bt601_limited = {
|
|
|
|
|
.matrix = {
|
|
|
|
|
{ 5020601039, 0, 6881764740 },
|
|
|
|
|
{ 5020601039, -1689204679, -3505362278 },
|
|
|
|
|
{ 5020601039, 8697922339, 0 },
|
|
|
|
|
},
|
|
|
|
|
.y_offset = 16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.709"],
|
|
|
|
|
* is_legal = False,
|
|
|
|
|
* bits = 8) * 2**32).astype(int)
|
|
|
|
|
*/
|
|
|
|
|
static const struct conversion_matrix yuv_bt709_full = {
|
|
|
|
|
.matrix = {
|
|
|
|
|
{ 4294967296, 0, 6763714498 },
|
|
|
|
|
{ 4294967296, -804551626, -2010578443 },
|
|
|
|
|
{ 4294967296, 7969741314, 0 },
|
|
|
|
|
},
|
|
|
|
|
.y_offset = 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.709"],
|
|
|
|
|
* is_legal = True,
|
|
|
|
|
* bits = 8) * 2**32).astype(int)
|
|
|
|
|
*/
|
|
|
|
|
static const struct conversion_matrix yuv_bt709_limited = {
|
|
|
|
|
.matrix = {
|
|
|
|
|
{ 5020601039, 0, 7729959424 },
|
|
|
|
|
{ 5020601039, -919487572, -2297803934 },
|
|
|
|
|
{ 5020601039, 9108275786, 0 },
|
|
|
|
|
},
|
|
|
|
|
.y_offset = 16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.2020"],
|
|
|
|
|
* is_legal = False,
|
|
|
|
|
* bits = 8) * 2**32).astype(int)
|
|
|
|
|
*/
|
|
|
|
|
static const struct conversion_matrix yuv_bt2020_full = {
|
|
|
|
|
.matrix = {
|
|
|
|
|
{ 4294967296, 0, 6333358775 },
|
|
|
|
|
{ 4294967296, -706750298, -2453942994 },
|
|
|
|
|
{ 4294967296, 8080551471, 0 },
|
|
|
|
|
},
|
|
|
|
|
.y_offset = 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* numpy.around(colour.matrix_YCbCr(K=colour.WEIGHTS_YCBCR["ITU-R BT.2020"],
|
|
|
|
|
* is_legal = True,
|
|
|
|
|
* bits = 8) * 2**32).astype(int)
|
|
|
|
|
*/
|
|
|
|
|
static const struct conversion_matrix yuv_bt2020_limited = {
|
|
|
|
|
.matrix = {
|
|
|
|
|
{ 5020601039, 0, 7238124312 },
|
|
|
|
|
{ 5020601039, -807714626, -2804506279 },
|
|
|
|
|
{ 5020601039, 9234915964, 0 },
|
|
|
|
|
},
|
|
|
|
|
.y_offset = 16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* swap_uv_columns() - Swap u and v column of a given matrix
|
|
|
|
|
*
|
|
|
|
|
* @matrix: Matrix in which column are swapped
|
|
|
|
|
*/
|
|
|
|
|
static void swap_uv_columns(struct conversion_matrix *matrix)
|
|
|
|
|
{
|
|
|
|
|
swap(matrix->matrix[0][2], matrix->matrix[0][1]);
|
|
|
|
|
swap(matrix->matrix[1][2], matrix->matrix[1][1]);
|
|
|
|
|
swap(matrix->matrix[2][2], matrix->matrix[2][1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* get_conversion_matrix_to_argb_u16() - Retrieve the correct yuv to rgb conversion matrix for a
|
|
|
|
|
* given encoding and range.
|
|
|
|
|
*
|
|
|
|
|
* @format: DRM_FORMAT_* value for which to obtain a conversion function (see [drm_fourcc.h])
|
|
|
|
|
* @encoding: DRM_COLOR_* value for which to obtain a conversion matrix
|
|
|
|
|
* @range: DRM_COLOR_*_RANGE value for which to obtain a conversion matrix
|
|
|
|
|
* @matrix: Pointer to store the value into
|
|
|
|
|
*/
|
|
|
|
|
void get_conversion_matrix_to_argb_u16(u32 format,
|
|
|
|
|
enum drm_color_encoding encoding,
|
|
|
|
|
enum drm_color_range range,
|
|
|
|
|
struct conversion_matrix *matrix)
|
|
|
|
|
{
|
|
|
|
|
const struct conversion_matrix *matrix_to_copy;
|
|
|
|
|
bool limited_range;
|
|
|
|
|
|
|
|
|
|
switch (range) {
|
|
|
|
|
case DRM_COLOR_YCBCR_LIMITED_RANGE:
|
|
|
|
|
limited_range = true;
|
|
|
|
|
break;
|
|
|
|
|
case DRM_COLOR_YCBCR_FULL_RANGE:
|
|
|
|
|
limited_range = false;
|
|
|
|
|
break;
|
|
|
|
|
case DRM_COLOR_RANGE_MAX:
|
|
|
|
|
limited_range = false;
|
|
|
|
|
WARN_ONCE(true, "The requested range is not supported.");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (encoding) {
|
|
|
|
|
case DRM_COLOR_YCBCR_BT601:
|
|
|
|
|
matrix_to_copy = limited_range ? &yuv_bt601_limited :
|
|
|
|
|
&yuv_bt601_full;
|
|
|
|
|
break;
|
|
|
|
|
case DRM_COLOR_YCBCR_BT709:
|
|
|
|
|
matrix_to_copy = limited_range ? &yuv_bt709_limited :
|
|
|
|
|
&yuv_bt709_full;
|
|
|
|
|
break;
|
|
|
|
|
case DRM_COLOR_YCBCR_BT2020:
|
|
|
|
|
matrix_to_copy = limited_range ? &yuv_bt2020_limited :
|
|
|
|
|
&yuv_bt2020_full;
|
|
|
|
|
break;
|
|
|
|
|
case DRM_COLOR_ENCODING_MAX:
|
|
|
|
|
matrix_to_copy = &no_operation;
|
|
|
|
|
WARN_ONCE(true, "The requested encoding is not supported.");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(matrix, matrix_to_copy, sizeof(*matrix_to_copy));
|
|
|
|
|
|
|
|
|
|
switch (format) {
|
|
|
|
|
case DRM_FORMAT_YVU420:
|
|
|
|
|
case DRM_FORMAT_YVU422:
|
|
|
|
|
case DRM_FORMAT_YVU444:
|
|
|
|
|
case DRM_FORMAT_NV21:
|
|
|
|
|
case DRM_FORMAT_NV61:
|
|
|
|
|
case DRM_FORMAT_NV42:
|
|
|
|
|
swap_uv_columns(matrix);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(get_conversion_matrix_to_argb_u16);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* get_pixel_write_function() - Retrieve the correct write_pixel function for a specific format.
|
|
|
|
|
* The returned pointer is NULL for unsupported pixel formats. The caller must ensure that the
|
|
|
|
|