#include <linux/kernel.h>
#include <linux/minmax.h>
#include <drm/drm_blend.h>
#include <drm/drm_rect.h>
#include <drm/drm_fixed.h>
#include <kunit/visibility.h>
#include "vkms_formats.h"
static void packed_pixels_offset(const struct 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;
const struct drm_format_info *format = frame_info->fb->format;
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];
}
static void packed_pixels_addr(const struct vkms_frame_info *frame_info,
int x, int y, int plane_index, u8 **addr, int *rem_x,
int *rem_y)
{
int offset;
packed_pixels_offset(frame_info, x, y, plane_index, &offset, rem_x, rem_y);
*addr = (u8 *)frame_info->map[0].vaddr + offset;
}
static int 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;
}
static void packed_pixels_addr_1x1(const struct 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__);
packed_pixels_offset(frame_info, x, y, plane_index, &offset, &rem_x,
&rem_y);
*addr = (u8 *)frame_info->map[0].vaddr + offset;
}
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;
}
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;
}
static struct pixel_argb_u16 argb_u16_from_u8888(u8 a, u8 r, u8 g, u8 b)
{
struct pixel_argb_u16 out_pixel;
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;
}
static struct pixel_argb_u16 argb_u16_from_u16161616(u16 a, u16 r, u16 g, u16 b)
{
struct pixel_argb_u16 out_pixel;
out_pixel.a = a;
out_pixel.r = r;
out_pixel.g = g;
out_pixel.b = b;
return out_pixel;
}
static struct pixel_argb_u16 argb_u16_from_le16161616(__le16 a, __le16 r, __le16 g, __le16 b)
{
return argb_u16_from_u16161616(le16_to_cpu(a), le16_to_cpu(r), le16_to_cpu(g),
le16_to_cpu(b));
}
static struct pixel_argb_u16 argb_u16_from_RGB565(const __le16 *pixel)
{
struct pixel_argb_u16 out_pixel;
s64 fp_rb_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(31));
s64 fp_g_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(63));
u16 rgb_565 = le16_to_cpu(*pixel);
s64 fp_r = drm_int2fixp((rgb_565 >> 11) & 0x1f);
s64 fp_g = drm_int2fixp((rgb_565 >> 5) & 0x3f);
s64 fp_b = drm_int2fixp(rgb_565 & 0x1f);
out_pixel.a = (u16)0xffff;
out_pixel.r = drm_fixp2int_round(drm_fixp_mul(fp_r, fp_rb_ratio));
out_pixel.g = drm_fixp2int_round(drm_fixp_mul(fp_g, fp_g_ratio));
out_pixel.b = drm_fixp2int_round(drm_fixp_mul(fp_b, fp_rb_ratio));
return out_pixel;
}
static struct pixel_argb_u16 argb_u16_from_gray8(u8 gray)
{
return argb_u16_from_u8888(255, gray, gray, gray);
}
static struct pixel_argb_u16 argb_u16_from_grayu16(u16 gray)
{
return argb_u16_from_u16161616(0xFFFF, gray, gray, gray);
}
static struct pixel_argb_u16 argb_u16_from_BGR565(const __le16 *pixel)
{
struct pixel_argb_u16 out_pixel;
out_pixel = argb_u16_from_RGB565(pixel);
swap(out_pixel.r, out_pixel.b);
return out_pixel;
}
VISIBLE_IF_KUNIT
struct pixel_argb_u16 argb_u16_from_yuv161616(const struct conversion_matrix *matrix,
u16 y, u16 channel_1, u16 channel_2)
{
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);
}
EXPORT_SYMBOL_IF_KUNIT(argb_u16_from_yuv161616);
#define READ_LINE(function_name, pixel_name, pixel_type, callback, ...) \
static void function_name(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[]) \
{ \
struct pixel_argb_u16 *end = out_pixel + count; \
int step = get_block_step_bytes(plane->frame_info->fb, direction, 0); \
u8 *src_pixels; \
\
packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, &src_pixels); \
\
while (out_pixel < end) { \
pixel_type *(pixel_name) = (pixel_type *)src_pixels; \
*out_pixel = (callback)(__VA_ARGS__); \
out_pixel += 1; \
src_pixels += step; \
} \
}
#define READ_LINE_ARGB8888(function_name, pixel_name, a, r, g, b) \
READ_LINE(function_name, pixel_name, u8, argb_u16_from_u8888, a, r, g, b)
#define READ_LINE_le16161616(function_name, pixel_name, a, r, g, b) \
READ_LINE(function_name, pixel_name, __le16, argb_u16_from_le16161616, a, r, g, b)
static void Rx_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[])
{
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;
if (direction == READ_LEFT_TO_RIGHT) {
restart_bit_offset = 8 - bits_per_pixel;
step_bit_offset = -bits_per_pixel;
} else {
restart_bit_offset = 0;
step_bit_offset = bits_per_pixel;
}
while (out_pixel < end) {
u8 val = ((*src_pixels) >> bit_offset) & mask;
*out_pixel = argb_u16_from_grayu16((int)val * lum_per_level);
bit_offset += step_bit_offset;
if (bit_offset < 0 || 8 <= bit_offset) {
bit_offset = restart_bit_offset;
src_pixels += step;
}
out_pixel += 1;
}
} else if (direction == READ_TOP_TO_BOTTOM || direction == READ_BOTTOM_TO_TOP) {
while (out_pixel < end) {
u8 val = (*src_pixels >> bit_offset) & mask;
*out_pixel = argb_u16_from_grayu16((int)val * lum_per_level);
src_pixels += step;
out_pixel += 1;
}
}
}
static void R1_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[])
{
Rx_read_line(plane, x_start, y_start, direction, count, out_pixel);
}
static void R2_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[])
{
Rx_read_line(plane, x_start, y_start, direction, count, out_pixel);
}
static void R4_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[])
{
Rx_read_line(plane, x_start, y_start, direction, count, out_pixel);
}
READ_LINE_ARGB8888(XRGB8888_read_line, px, 0xFF, px[2], px[1], px[0])
READ_LINE_ARGB8888(XBGR8888_read_line, px, 0xFF, px[0], px[1], px[2])
READ_LINE_ARGB8888(ARGB8888_read_line, px, px[3], px[2], px[1], px[0])
READ_LINE_ARGB8888(ABGR8888_read_line, px, px[3], px[0], px[1], px[2])
READ_LINE_ARGB8888(RGBA8888_read_line, px, px[0], px[3], px[2], px[1])
READ_LINE_ARGB8888(BGRA8888_read_line, px, px[0], px[1], px[2], px[3])
READ_LINE_ARGB8888(RGB888_read_line, px, 0xFF, px[2], px[1], px[0])
READ_LINE_ARGB8888(BGR888_read_line, px, 0xFF, px[0], px[1], px[2])
READ_LINE_le16161616(ARGB16161616_read_line, px, px[3], px[2], px[1], px[0])
READ_LINE_le16161616(ABGR16161616_read_line, px, px[3], px[0], px[1], px[2])
READ_LINE_le16161616(XRGB16161616_read_line, px, cpu_to_le16(0xFFFF), px[2], px[1], px[0])
READ_LINE_le16161616(XBGR16161616_read_line, px, cpu_to_le16(0xFFFF), px[0], px[1], px[2])
READ_LINE(RGB565_read_line, px, __le16, argb_u16_from_RGB565, px)
READ_LINE(BGR565_read_line, px, __le16, argb_u16_from_BGR565, px)
READ_LINE(R8_read_line, px, u8, argb_u16_from_gray8, *px)
#define READ_LINE_YUV_SEMIPLANAR(function_name, pixel_1_name, pixel_2_name, pixel_1_type, \
pixel_2_type, callback, ...) \
static void function_name(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 *plane_1; \
u8 *plane_2; \
\
packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, \
&plane_1); \
packed_pixels_addr_1x1(plane->frame_info, \
x_start / plane->frame_info->fb->format->hsub, \
y_start / plane->frame_info->fb->format->vsub, 1, \
&plane_2); \
int step_1 = get_block_step_bytes(plane->frame_info->fb, direction, 0); \
int step_2 = 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++) { \
pixel_1_type *(pixel_1_name) = (pixel_1_type *)plane_1; \
pixel_2_type *(pixel_2_name) = (pixel_2_type *)plane_2; \
*out_pixel = (callback)(conversion_matrix, __VA_ARGS__); \
out_pixel += 1; \
plane_1 += step_1; \
if ((i + subsampling_offset + 1) % subsampling == 0) \
plane_2 += step_2; \
} \
}
READ_LINE_YUV_SEMIPLANAR(YUV888_semiplanar_read_line, y, uv, u8, u8, argb_u16_from_yuv161616,
y[0] * 257, uv[0] * 257, uv[1] * 257)
READ_LINE_YUV_SEMIPLANAR(YUV161616_semiplanar_read_line, y, uv, u16, u16, argb_u16_from_yuv161616,
y[0], uv[0], uv[1])
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_yuv161616(conversion_matrix,
*y_plane * 257, *channel_1_plane * 257,
*channel_2_plane * 257);
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;
}
}
}
static void argb_u16_to_ARGB8888(u8 *out_pixel, 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->r, 257);
out_pixel[1] = DIV_ROUND_CLOSEST(in_pixel->g, 257);
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)
{
out_pixel[3] = 0xff;
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);
}
static void argb_u16_to_ABGR8888(u8 *out_pixel, 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);
out_pixel[1] = DIV_ROUND_CLOSEST(in_pixel->g, 257);
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)
{
__le16 *pixel = (__le16 *)out_pixel;
pixel[3] = cpu_to_le16(in_pixel->a);
pixel[2] = cpu_to_le16(in_pixel->r);
pixel[1] = cpu_to_le16(in_pixel->g);
pixel[0] = cpu_to_le16(in_pixel->b);
}
static void argb_u16_to_XRGB16161616(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
{
__le16 *pixel = (__le16 *)out_pixel;
pixel[3] = cpu_to_le16(0xffff);
pixel[2] = cpu_to_le16(in_pixel->r);
pixel[1] = cpu_to_le16(in_pixel->g);
pixel[0] = cpu_to_le16(in_pixel->b);
}
static void argb_u16_to_RGB565(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
{
__le16 *pixel = (__le16 *)out_pixel;
s64 fp_rb_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(31));
s64 fp_g_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(63));
s64 fp_r = drm_int2fixp(in_pixel->r);
s64 fp_g = drm_int2fixp(in_pixel->g);
s64 fp_b = drm_int2fixp(in_pixel->b);
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);
}
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);
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]);
}
pixel_read_line_t get_pixel_read_line_function(u32 format)
{
switch (format) {
case DRM_FORMAT_ARGB8888:
return &ARGB8888_read_line;
case DRM_FORMAT_ABGR8888:
return &ABGR8888_read_line;
case DRM_FORMAT_BGRA8888:
return &BGRA8888_read_line;
case DRM_FORMAT_RGBA8888:
return &RGBA8888_read_line;
case DRM_FORMAT_XRGB8888:
return &XRGB8888_read_line;
case DRM_FORMAT_XBGR8888:
return &XBGR8888_read_line;
case DRM_FORMAT_RGB888:
return &RGB888_read_line;
case DRM_FORMAT_BGR888:
return &BGR888_read_line;
case DRM_FORMAT_ARGB16161616:
return &ARGB16161616_read_line;
case DRM_FORMAT_ABGR16161616:
return &ABGR16161616_read_line;
case DRM_FORMAT_XRGB16161616:
return &XRGB16161616_read_line;
case DRM_FORMAT_XBGR16161616:
return &XBGR16161616_read_line;
case DRM_FORMAT_RGB565:
return &RGB565_read_line;
case DRM_FORMAT_BGR565:
return &BGR565_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 &YUV888_semiplanar_read_line;
case DRM_FORMAT_P010:
case DRM_FORMAT_P012:
case DRM_FORMAT_P016:
return &YUV161616_semiplanar_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:
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();
}
}
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,
};
static const struct conversion_matrix yuv_bt601_limited = {
.matrix = {
{ 5020601039, 0, 6881764740 },
{ 5020601039, -1689204679, -3505362278 },
{ 5020601039, 8697922339, 0 },
},
.y_offset = 16,
};
static const struct conversion_matrix yuv_bt709_full = {
.matrix = {
{ 4294967296, 0, 6763714498 },
{ 4294967296, -804551626, -2010578443 },
{ 4294967296, 7969741314, 0 },
},
.y_offset = 0,
};
static const struct conversion_matrix yuv_bt709_limited = {
.matrix = {
{ 5020601039, 0, 7729959424 },
{ 5020601039, -919487572, -2297803934 },
{ 5020601039, 9108275786, 0 },
},
.y_offset = 16,
};
static const struct conversion_matrix yuv_bt2020_full = {
.matrix = {
{ 4294967296, 0, 6333358775 },
{ 4294967296, -706750298, -2453942994 },
{ 4294967296, 8080551471, 0 },
},
.y_offset = 0,
};
static const struct conversion_matrix yuv_bt2020_limited = {
.matrix = {
{ 5020601039, 0, 7238124312 },
{ 5020601039, -807714626, -2804506279 },
{ 5020601039, 9234915964, 0 },
},
.y_offset = 16,
};
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]);
}
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);
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:
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();
}
}