/** * packed_pixels_offset() - Get the offset of the block containing the pixel at coordinates x/y * * @frame_info: Buffer metadata * @x: The x coordinate of the wanted pixel in the buffer * @y: The y coordinate of the wanted pixel in the buffer * @plane_index: The index of the plane to use * @offset: The returned offset inside the buffer of the block * @rem_x: The returned X coordinate of the requested pixel in the block * @rem_y: The returned Y coordinate of the requested pixel in the block * * As some pixel formats store multiple pixels in a block (DRM_FORMAT_R* for example), some * pixels are not individually addressable. This function return 3 values: the offset of the * whole block, and the coordinate of the requested pixel inside this block. * For example, if the format is DRM_FORMAT_R1 and the requested coordinate is 13,5, the offset * will point to the byte 5*pitches + 13/8 (second byte of the 5th line), and the rem_x/rem_y * coordinates will be (13 % 8, 5 % 1) = (5, 0) * * With this function, the caller just have to extract the correct pixel from the block.
*/ staticvoid packed_pixels_offset(conststruct vkms_frame_info *frame_info, int x, int y, int plane_index, int *offset, int *rem_x, int *rem_y)
{ struct drm_framebuffer *fb = frame_info->fb; conststruct drm_format_info *format = frame_info->fb->format; /* Directly using x and y to multiply pitches and format->ccp is not sufficient because * in some formats a block can represent multiple pixels. * * Dividing x and y by the block size allows to extract the correct offset of the block * containing the pixel.
*/
int block_x = x / drm_format_info_block_width(format, plane_index); int block_y = y / drm_format_info_block_height(format, plane_index); int block_pitch = fb->pitches[plane_index] * drm_format_info_block_height(format,
plane_index);
*rem_x = x % drm_format_info_block_width(format, plane_index);
*rem_y = y % drm_format_info_block_height(format, plane_index);
*offset = fb->offsets[plane_index] +
block_y * block_pitch +
block_x * format->char_per_block[plane_index];
}
/** * packed_pixels_addr() - Get the pointer to the block containing the pixel at the given * coordinates * * @frame_info: Buffer metadata * @x: The x (width) coordinate inside the plane * @y: The y (height) coordinate inside the plane * @plane_index: The index of the plane * @addr: The returned pointer * @rem_x: The returned X coordinate of the requested pixel in the block * @rem_y: The returned Y coordinate of the requested pixel in the block * * Takes the information stored in the frame_info, a pair of coordinates, and returns the address * of the block containing this pixel and the pixel position inside this block. * * See @packed_pixels_offset for details about rem_x/rem_y behavior.
*/ staticvoid packed_pixels_addr(conststruct vkms_frame_info *frame_info, int x, int y, int plane_index, u8 **addr, int *rem_x, int *rem_y)
{ int offset;
/** * get_block_step_bytes() - Common helper to compute the correct step value between each pixel block * to read in a certain direction. * * @fb: Framebuffer to iter on * @direction: Direction of the reading * @plane_index: Plane to get the step from * * As the returned count is the number of bytes between two consecutive blocks in a direction, * the caller may have to read multiple pixels before using the next one (for example, to read from * left to right in a DRM_FORMAT_R1 plane, each block contains 8 pixels, so the step must be used * only every 8 pixels).
*/ staticint get_block_step_bytes(struct drm_framebuffer *fb, enum pixel_read_direction direction, int plane_index)
{ switch (direction) { case READ_LEFT_TO_RIGHT: return fb->format->char_per_block[plane_index]; case READ_RIGHT_TO_LEFT: return -fb->format->char_per_block[plane_index]; case READ_TOP_TO_BOTTOM: return (int)fb->pitches[plane_index] * drm_format_info_block_width(fb->format,
plane_index); case READ_BOTTOM_TO_TOP: return -(int)fb->pitches[plane_index] * drm_format_info_block_width(fb->format,
plane_index);
}
return 0;
}
/** * packed_pixels_addr_1x1() - Get the pointer to the block containing the pixel at the given * coordinates * * @frame_info: Buffer metadata * @x: The x (width) coordinate inside the plane * @y: The y (height) coordinate inside the plane * @plane_index: The index of the plane * @addr: The returned pointer * * This function can only be used with format where block_h == block_w == 1.
*/ staticvoid packed_pixels_addr_1x1(conststruct vkms_frame_info *frame_info, int x, int y, int plane_index, u8 **addr)
{ int offset, rem_x, rem_y;
WARN_ONCE(drm_format_info_block_width(frame_info->fb->format,
plane_index) != 1, "%s() only support formats with block_w == 1", __func__);
WARN_ONCE(drm_format_info_block_height(frame_info->fb->format,
plane_index) != 1, "%s() only support formats with block_h == 1", __func__);
/** * get_subsampling() - Get the subsampling divisor value on a specific direction * * @format: format to extarct the subsampling from * @direction: direction of the subsampling requested
*/ staticint get_subsampling(conststruct 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
*/ staticint get_subsampling_offset(enum pixel_read_direction direction, int x_start, inty_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 * * They are used in the `read_line`s functions to avoid duplicate work for some pixel formats.
*/
staticstruct pixel_argb_u16 argb_u16_from_u8888(u8 a, u8 r, u8 g, u8 b)
{ struct pixel_argb_u16 out_pixel; /* * The 257 is the "conversion ratio". This number is obtained by the * (2^16 - 1) / (2^8 - 1) division. Which, in this case, tries to get * the best color value in a pixel format with more possibilities. * A similar idea applies to others RGB color conversions.
*/
out_pixel.a = (u16)a * 257;
out_pixel.r = (u16)r * 257;
out_pixel.g = (u16)g * 257;
out_pixel.b = (u16)b * 257;
return out_pixel;
}
staticstruct pixel_argb_u16 argb_u16_from_u16161616(u16 a, u16 r, u16 g, u16 b)
{ struct pixel_argb_u16 out_pixel;
/* * The following functions are read_line function for each pixel format supported by VKMS. * * They read a line starting at the point @x_start,@y_start following the @direction. The result * is stored in @out_pixel and in a 64 bits format, see struct pixel_argb_u16. * * These functions are very repetitive, but the innermost pixel loops must be kept inside these * functions for performance reasons. Some benchmarking was done in [1] where having the innermost * loop factored out of these functions showed a slowdown by a factor of three. * * [1]: https://lore.kernel.org/dri-devel/d258c8dc-78e9-4509-9037-a98f7f33b3a3@riseup.net/
*/
staticvoid Rx_read_line(conststruct vkms_plane_state *plane, int x_start, int y_start, enum pixel_read_direction direction, int count, struct pixel_argb_u16 out_pixel[])
{ struct pixel_argb_u16 *end = out_pixel + count; int bits_per_pixel = drm_format_info_bpp(plane->frame_info->fb->format, 0);
u8 *src_pixels; int rem_x, rem_y;
WARN_ONCE(drm_format_info_block_height(plane->frame_info->fb->format, 0) != 1, "%s() only support formats with block_h == 1", __func__);
packed_pixels_addr(plane->frame_info, x_start, y_start, 0, &src_pixels, &rem_x, &rem_y); int bit_offset = (8 - bits_per_pixel) - rem_x * bits_per_pixel; int step = get_block_step_bytes(plane->frame_info->fb, direction, 0); int mask = (0x1 << bits_per_pixel) - 1; int lum_per_level = 0xFFFF / mask;
if (direction == READ_LEFT_TO_RIGHT || direction == READ_RIGHT_TO_LEFT) { int restart_bit_offset; int step_bit_offset;
/* * 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)
*/ staticvoid semi_planar_yuv_read_line(conststruct 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;
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)
*/ staticvoid planar_yuv_read_line(conststruct 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;
/* * The following functions take one &struct pixel_argb_u16 and convert it to a specific format. * The result is stored in @out_pixel. * * They are used in vkms_writeback_row() to convert and store a pixel from the src_buffer to * the writeback buffer.
*/ staticvoid argb_u16_to_ARGB8888(u8 *out_pixel, conststruct pixel_argb_u16 *in_pixel)
{ /* * This sequence below is important because the format's byte order is * in little-endian. In the case of the ARGB8888 the memory is * organized this way: * * | Addr | = blue channel * | Addr + 1 | = green channel * | Addr + 2 | = Red channel * | Addr + 3 | = Alpha channel
*/
out_pixel[3] = DIV_ROUND_CLOSEST(in_pixel->a, 257);
out_pixel[2] = DIV_ROUND_CLOSEST(in_pixel->r, 257);
out_pixel[1] = DIV_ROUND_CLOSEST(in_pixel->g, 257);
out_pixel[0] = DIV_ROUND_CLOSEST(in_pixel->b, 257);
}
u16 r = drm_fixp2int(drm_fixp_div(fp_r, fp_rb_ratio));
u16 g = drm_fixp2int(drm_fixp_div(fp_g, fp_g_ratio));
u16 b = drm_fixp2int(drm_fixp_div(fp_b, fp_rb_ratio));
*pixel = cpu_to_le16(r << 11 | g << 5 | b);
}
/** * 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. * * @wb: Job where to insert the final image * @src_buffer: Line to write * @y: Row to write in the writeback buffer
*/ void vkms_writeback_row(struct vkms_writeback_job *wb, conststruct 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); 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);
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]);
}
/** * get_pixel_read_line_function() - Retrieve the correct read_line function for a specific * format. The returned pointer is NULL for unsupported pixel formats. The caller must ensure that * the pointer is valid before using it in a vkms_plane_state. * * @format: DRM_FORMAT_* value for which to obtain a conversion function (see [drm_fourcc.h])
*/
pixel_read_line_t get_pixel_read_line_function(u32 format)
{ switch (format) { case DRM_FORMAT_ARGB8888: return &ARGB8888_read_line; case DRM_FORMAT_XRGB8888: return &XRGB8888_read_line; case DRM_FORMAT_ABGR8888: return &ABGR8888_read_line; case DRM_FORMAT_ARGB16161616: return &ARGB16161616_read_line; case DRM_FORMAT_XRGB16161616: 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; case DRM_FORMAT_R1: return &R1_read_line; case DRM_FORMAT_R2: return &R2_read_line; case DRM_FORMAT_R4: return &R4_read_line; case DRM_FORMAT_R8: return &R8_read_line; default: /* * This is a bug in vkms_plane_atomic_check(). All the supported * format must: * - Be listed in vkms_formats in vkms_plane.c * - Have a pixel_read callback defined here
*/
pr_err("Pixel format %p4cc is not supported by VKMS planes. This is a kernel bug, atomic check must forbid this configuration.\n",
&format);
BUG();
}
}
/* * 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)
*/ staticconststruct conversion_matrix no_operation = {
.matrix = {
{ 4294967296, 0, 0, },
{ 0, 4294967296, 0, },
{ 0, 0, 4294967296, },
},
.y_offset = 0,
};
/** * swap_uv_columns() - Swap u and v column of a given matrix * * @matrix: Matrix in which column are swapped
*/ staticvoid 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)
{ conststruct 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;
}
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 * pointer is valid before using it in a vkms_writeback_job. * * @format: DRM_FORMAT_* value for which to obtain a conversion function (see [drm_fourcc.h])
*/
pixel_write_t get_pixel_write_function(u32 format)
{ switch (format) { case DRM_FORMAT_ARGB8888: return &argb_u16_to_ARGB8888; case DRM_FORMAT_XRGB8888: return &argb_u16_to_XRGB8888; case DRM_FORMAT_ABGR8888: return &argb_u16_to_ABGR8888; case DRM_FORMAT_ARGB16161616: return &argb_u16_to_ARGB16161616; case DRM_FORMAT_XRGB16161616: return &argb_u16_to_XRGB16161616; case DRM_FORMAT_RGB565: return &argb_u16_to_RGB565; default: /* * This is a bug in vkms_writeback_atomic_check. All the supported * format must: * - Be listed in vkms_wb_formats in vkms_writeback.c * - Have a pixel_write callback defined here
*/
pr_err("Pixel format %p4cc is not supported by VKMS writeback. This is a kernel bug, atomic check must forbid this configuration.\n",
&format);
BUG();
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.