diff options
Diffstat (limited to 'drivers/gpu/drm/vkms')
| -rw-r--r-- | drivers/gpu/drm/vkms/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/vkms/vkms_composer.c | 314 | ||||
| -rw-r--r-- | drivers/gpu/drm/vkms/vkms_drv.h | 33 | ||||
| -rw-r--r-- | drivers/gpu/drm/vkms/vkms_formats.c | 286 | ||||
| -rw-r--r-- | drivers/gpu/drm/vkms/vkms_formats.h | 12 | ||||
| -rw-r--r-- | drivers/gpu/drm/vkms/vkms_plane.c | 56 | ||||
| -rw-r--r-- | drivers/gpu/drm/vkms/vkms_writeback.c | 39 |
7 files changed, 524 insertions, 217 deletions
diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index 72f779cbfedd..1b28a6a32948 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile @@ -3,6 +3,7 @@ vkms-y := \ vkms_drv.o \ vkms_plane.o \ vkms_output.o \ + vkms_formats.o \ vkms_crtc.o \ vkms_composer.o \ vkms_writeback.o diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 775b97766e08..8e53fa80742b 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -7,203 +7,185 @@ #include <drm/drm_fourcc.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_vblank.h> +#include <linux/minmax.h> #include "vkms_drv.h" -static u32 get_pixel_from_buffer(int x, int y, const u8 *buffer, - const struct vkms_composer *composer) +static u16 pre_mul_blend_channel(u16 src, u16 dst, u16 alpha) { - u32 pixel; - int src_offset = composer->offset + (y * composer->pitch) - + (x * composer->cpp); + u32 new_color; - pixel = *(u32 *)&buffer[src_offset]; + new_color = (src * 0xffff + dst * (0xffff - alpha)); - return pixel; + return DIV_ROUND_CLOSEST(new_color, 0xffff); } /** - * compute_crc - Compute CRC value on output frame + * pre_mul_alpha_blend - alpha blending equation + * @src_frame_info: source framebuffer's metadata + * @stage_buffer: The line with the pixels from src_plane + * @output_buffer: A line buffer that receives all the blends output * - * @vaddr: address to final framebuffer - * @composer: framebuffer's metadata + * Using the information from the `frame_info`, this blends only the + * necessary pixels from the `stage_buffer` to the `output_buffer` + * using premultiplied blend formula. * - * returns CRC value computed using crc32 on the visible portion of - * the final framebuffer at vaddr_out + * The current DRM assumption is that pixel color values have been already + * pre-multiplied with the alpha channel values. See more + * drm_plane_create_blend_mode_property(). Also, this formula assumes a + * completely opaque background. */ -static uint32_t compute_crc(const u8 *vaddr, - const struct vkms_composer *composer) +static void pre_mul_alpha_blend(struct vkms_frame_info *frame_info, + struct line_buffer *stage_buffer, + struct line_buffer *output_buffer) { - int x, y; - u32 crc = 0, pixel = 0; - int x_src = composer->src.x1 >> 16; - int y_src = composer->src.y1 >> 16; - int h_src = drm_rect_height(&composer->src) >> 16; - int w_src = drm_rect_width(&composer->src) >> 16; - - for (y = y_src; y < y_src + h_src; ++y) { - for (x = x_src; x < x_src + w_src; ++x) { - pixel = get_pixel_from_buffer(x, y, vaddr, composer); - crc = crc32_le(crc, (void *)&pixel, sizeof(u32)); - } + int x_dst = frame_info->dst.x1; + struct pixel_argb_u16 *out = output_buffer->pixels + x_dst; + struct pixel_argb_u16 *in = stage_buffer->pixels; + int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), + stage_buffer->n_pixels); + + for (int x = 0; x < x_limit; x++) { + out[x].a = (u16)0xffff; + out[x].r = pre_mul_blend_channel(in[x].r, out[x].r, in[x].a); + out[x].g = pre_mul_blend_channel(in[x].g, out[x].g, in[x].a); + out[x].b = pre_mul_blend_channel(in[x].b, out[x].b, in[x].a); } - - return crc; } -static u8 blend_channel(u8 src, u8 dst, u8 alpha) +static bool check_y_limit(struct vkms_frame_info *frame_info, int y) { - u32 pre_blend; - u8 new_color; - - pre_blend = (src * 255 + dst * (255 - alpha)); - - /* Faster div by 255 */ - new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8); + if (y >= frame_info->dst.y1 && y < frame_info->dst.y2) + return true; - return new_color; + return false; } -/** - * alpha_blend - alpha blending equation - * @argb_src: src pixel on premultiplied alpha mode - * @argb_dst: dst pixel completely opaque - * - * blend pixels using premultiplied blend formula. The current DRM assumption - * is that pixel color values have been already pre-multiplied with the alpha - * channel values. See more drm_plane_create_blend_mode_property(). Also, this - * formula assumes a completely opaque background. - */ -static void alpha_blend(const u8 *argb_src, u8 *argb_dst) +static void fill_background(const struct pixel_argb_u16 *background_color, + struct line_buffer *output_buffer) { - u8 alpha; - - alpha = argb_src[3]; - argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha); - argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha); - argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha); + for (size_t i = 0; i < output_buffer->n_pixels; i++) + output_buffer->pixels[i] = *background_color; } /** - * x_blend - blending equation that ignores the pixel alpha + * @wb_frame_info: The writeback frame buffer metadata + * @crtc_state: The crtc state + * @crc32: The crc output of the final frame + * @output_buffer: A buffer of a row that will receive the result of the blend(s) + * @stage_buffer: The line with the pixels from plane being blend to the output * - * overwrites RGB color value from src pixel to dst pixel. + * This function blends the pixels (Using the `pre_mul_alpha_blend`) + * from all planes, calculates the crc32 of the output from the former step, + * and, if necessary, convert and store the output to the writeback buffer. */ -static void x_blend(const u8 *xrgb_src, u8 *xrgb_dst) +static void blend(struct vkms_writeback_job *wb, + struct vkms_crtc_state *crtc_state, + u32 *crc32, struct line_buffer *stage_buffer, + struct line_buffer *output_buffer, size_t row_size) { - memcpy(xrgb_dst, xrgb_src, sizeof(u8) * 3); -} + struct vkms_plane_state **plane = crtc_state->active_planes; + u32 n_active_planes = crtc_state->num_active_planes; -/** - * blend - blend value at vaddr_src with value at vaddr_dst - * @vaddr_dst: destination address - * @vaddr_src: source address - * @dst_composer: destination framebuffer's metadata - * @src_composer: source framebuffer's metadata - * @pixel_blend: blending equation based on plane format - * - * Blend the vaddr_src value with the vaddr_dst value using a pixel blend - * equation according to the supported plane formats DRM_FORMAT_(A/XRGB8888) - * and clearing alpha channel to an completely opaque background. This function - * uses buffer's metadata to locate the new composite values at vaddr_dst. - * - * TODO: completely clear the primary plane (a = 0xff) before starting to blend - * pixel color values - */ -static void blend(void *vaddr_dst, void *vaddr_src, - struct vkms_composer *dst_composer, - struct vkms_composer *src_composer, - void (*pixel_blend)(const u8 *, u8 *)) -{ - int i, j, j_dst, i_dst; - int offset_src, offset_dst; - u8 *pixel_dst, *pixel_src; - - int x_src = src_composer->src.x1 >> 16; - int y_src = src_composer->src.y1 >> 16; - - int x_dst = src_composer->dst.x1; - int y_dst = src_composer->dst.y1; - int h_dst = drm_rect_height(&src_composer->dst); - int w_dst = drm_rect_width(&src_composer->dst); - - int y_limit = y_src + h_dst; - int x_limit = x_src + w_dst; - - for (i = y_src, i_dst = y_dst; i < y_limit; ++i) { - for (j = x_src, j_dst = x_dst; j < x_limit; ++j) { - offset_dst = dst_composer->offset - + (i_dst * dst_composer->pitch) - + (j_dst++ * dst_composer->cpp); - offset_src = src_composer->offset - + (i * src_composer->pitch) - + (j * src_composer->cpp); - - pixel_src = (u8 *)(vaddr_src + offset_src); - pixel_dst = (u8 *)(vaddr_dst + offset_dst); - pixel_blend(pixel_src, pixel_dst); - /* clearing alpha channel (0xff)*/ - pixel_dst[3] = 0xff; + const struct pixel_argb_u16 background_color = { .a = 0xffff }; + + size_t crtc_y_limit = crtc_state->base.crtc->mode.vdisplay; + + for (size_t y = 0; y < crtc_y_limit; y++) { + fill_background(&background_color, output_buffer); + + /* The active planes are composed associatively in z-order. */ + for (size_t i = 0; i < n_active_planes; i++) { + if (!check_y_limit(plane[i]->frame_info, y)) + continue; + + plane[i]->plane_read(stage_buffer, plane[i]->frame_info, y); + pre_mul_alpha_blend(plane[i]->frame_info, stage_buffer, + output_buffer); } - i_dst++; + + *crc32 = crc32_le(*crc32, (void *)output_buffer->pixels, row_size); + + if (wb) + wb->wb_write(&wb->wb_frame_info, output_buffer, y); } } -static void compose_plane(struct vkms_composer *primary_composer, - struct vkms_composer *plane_composer, - void *vaddr_out) +static int check_format_funcs(struct vkms_crtc_state *crtc_state, + struct vkms_writeback_job *active_wb) { - struct drm_framebuffer *fb = &plane_composer->fb; - void *vaddr; - void (*pixel_blend)(const u8 *p_src, u8 *p_dst); + struct vkms_plane_state **planes = crtc_state->active_planes; + u32 n_active_planes = crtc_state->num_active_planes; - if (WARN_ON(iosys_map_is_null(&plane_composer->map[0]))) - return; + for (size_t i = 0; i < n_active_planes; i++) + if (!planes[i]->plane_read) + return -1; - vaddr = plane_composer->map[0].vaddr; + if (active_wb && !active_wb->wb_write) + return -1; - if (fb->format->format == DRM_FORMAT_ARGB8888) - pixel_blend = &alpha_blend; - else - pixel_blend = &x_blend; + return 0; +} + +static int check_iosys_map(struct vkms_crtc_state *crtc_state) +{ + struct vkms_plane_state **plane_state = crtc_state->active_planes; + u32 n_active_planes = crtc_state->num_active_planes; + + for (size_t i = 0; i < n_active_planes; i++) + if (iosys_map_is_null(&plane_state[i]->frame_info->map[0])) + return -1; - blend(vaddr_out, vaddr, primary_composer, plane_composer, pixel_blend); + return 0; } -static int compose_active_planes(void **vaddr_out, - struct vkms_composer *primary_composer, - struct vkms_crtc_state *crtc_state) +static int compose_active_planes(struct vkms_writeback_job *active_wb, + struct vkms_crtc_state *crtc_state, + u32 *crc32) { - struct drm_framebuffer *fb = &primary_composer->fb; - struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0); - const void *vaddr; - int i; - - if (!*vaddr_out) { - *vaddr_out = kvzalloc(gem_obj->size, GFP_KERNEL); - if (!*vaddr_out) { - DRM_ERROR("Cannot allocate memory for output frame."); - return -ENOMEM; - } - } + size_t line_width, pixel_size = sizeof(struct pixel_argb_u16); + struct line_buffer output_buffer, stage_buffer; + int ret = 0; + + /* + * This check exists so we can call `crc32_le` for the entire line + * instead doing it for each channel of each pixel in case + * `struct `pixel_argb_u16` had any gap added by the compiler + * between the struct fields. + */ + static_assert(sizeof(struct pixel_argb_u16) == 8); - if (WARN_ON(iosys_map_is_null(&primary_composer->map[0]))) + if (WARN_ON(check_iosys_map(crtc_state))) return -EINVAL; - vaddr = primary_composer->map[0].vaddr; + if (WARN_ON(check_format_funcs(crtc_state, active_wb))) + return -EINVAL; - memcpy(*vaddr_out, vaddr, gem_obj->size); + line_width = crtc_state->base.crtc->mode.hdisplay; + stage_buffer.n_pixels = line_width; + output_buffer.n_pixels = line_width; - /* If there are other planes besides primary, we consider the active - * planes should be in z-order and compose them associatively: - * ((primary <- overlay) <- cursor) - */ - for (i = 1; i < crtc_state->num_active_planes; i++) - compose_plane(primary_composer, - crtc_state->active_planes[i]->composer, - *vaddr_out); + stage_buffer.pixels = kvmalloc(line_width * pixel_size, GFP_KERNEL); + if (!stage_buffer.pixels) { + DRM_ERROR("Cannot allocate memory for the output line buffer"); + return -ENOMEM; + } - return 0; + output_buffer.pixels = kvmalloc(line_width * pixel_size, GFP_KERNEL); + if (!output_buffer.pixels) { + DRM_ERROR("Cannot allocate memory for intermediate line buffer"); + ret = -ENOMEM; + goto free_stage_buffer; + } + + blend(active_wb, crtc_state, crc32, &stage_buffer, + &output_buffer, line_width * pixel_size); + + kvfree(output_buffer.pixels); +free_stage_buffer: + kvfree(stage_buffer.pixels); + + return ret; } /** @@ -221,13 +203,11 @@ void vkms_composer_worker(struct work_struct *work) struct vkms_crtc_state, composer_work); struct drm_crtc *crtc = crtc_state->base.crtc; + struct vkms_writeback_job *active_wb = crtc_state->active_writeback; struct vkms_output *out = drm_crtc_to_vkms_output(crtc); - struct vkms_composer *primary_composer = NULL; - struct vkms_plane_state *act_plane = NULL; bool crc_pending, wb_pending; - void *vaddr_out = NULL; - u32 crc32 = 0; u64 frame_start, frame_end; + u32 crc32 = 0; int ret; spin_lock_irq(&out->composer_lock); @@ -247,35 +227,19 @@ void vkms_composer_worker(struct work_struct *work) if (!crc_pending) return; - if (crtc_state->num_active_planes >= 1) { - act_plane = crtc_state->active_planes[0]; - if (act_plane->base.base.plane->type == DRM_PLANE_TYPE_PRIMARY) - primary_composer = act_plane->composer; - } - - if (!primary_composer) - return; - if (wb_pending) - vaddr_out = crtc_state->active_writeback->data[0].vaddr; + ret = compose_active_planes(active_wb, crtc_state, &crc32); + else + ret = compose_active_planes(NULL, crtc_state, &crc32); - ret = compose_active_planes(&vaddr_out, primary_composer, - crtc_state); - if (ret) { - if (ret == -EINVAL && !wb_pending) - kvfree(vaddr_out); + if (ret) return; - } - - crc32 = compute_crc(vaddr_out, primary_composer); if (wb_pending) { drm_writeback_signal_completion(&out->wb_connector, 0); spin_lock_irq(&out->composer_lock); crtc_state->wb_pending = false; spin_unlock_irq(&out->composer_lock); - } else { - kvfree(vaddr_out); } /* diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 1d60654b553b..0a67b8073f7e 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -23,28 +23,41 @@ #define NUM_OVERLAY_PLANES 8 -struct vkms_writeback_job { - struct iosys_map map[DRM_FORMAT_MAX_PLANES]; - struct iosys_map data[DRM_FORMAT_MAX_PLANES]; -}; - -struct vkms_composer { - struct drm_framebuffer fb; +struct vkms_frame_info { + struct drm_framebuffer *fb; struct drm_rect src, dst; - struct iosys_map map[4]; + struct iosys_map map[DRM_FORMAT_MAX_PLANES]; unsigned int offset; unsigned int pitch; unsigned int cpp; }; +struct pixel_argb_u16 { + u16 a, r, g, b; +}; + +struct line_buffer { + size_t n_pixels; + struct pixel_argb_u16 *pixels; +}; + +struct vkms_writeback_job { + struct iosys_map data[DRM_FORMAT_MAX_PLANES]; + struct vkms_frame_info wb_frame_info; + void (*wb_write)(struct vkms_frame_info *frame_info, + const struct line_buffer *buffer, int y); +}; + /** * vkms_plane_state - Driver specific plane state * @base: base plane state - * @composer: data required for composing computation + * @frame_info: data required for composing computation */ struct vkms_plane_state { struct drm_shadow_plane_state base; - struct vkms_composer *composer; + struct vkms_frame_info *frame_info; + void (*plane_read)(struct line_buffer *buffer, + const struct vkms_frame_info *frame_info, int y); }; struct vkms_plane { diff --git a/drivers/gpu/drm/vkms/vkms_formats.c b/drivers/gpu/drm/vkms/vkms_formats.c new file mode 100644 index 000000000000..d4950688b3f1 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_formats.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/kernel.h> +#include <linux/minmax.h> +#include <drm/drm_rect.h> +#include <drm/drm_fixed.h> + +#include "vkms_formats.h" + +static size_t pixel_offset(const struct vkms_frame_info *frame_info, int x, int y) +{ + return frame_info->offset + (y * frame_info->pitch) + + (x * frame_info->cpp); +} + +/* + * packed_pixels_addr - Get the pointer to pixel of a given pair of coordinates + * + * @frame_info: Buffer metadata + * @x: The x(width) coordinate of the 2D buffer + * @y: The y(Heigth) coordinate of the 2D buffer + * + * Takes the information stored in the frame_info, a pair of coordinates, and + * returns the address of the first color channel. + * This function assumes the channels are packed together, i.e. a color channel + * comes immediately after another in the memory. And therefore, this function + * doesn't work for YUV with chroma subsampling (e.g. YUV420 and NV21). + */ +static void *packed_pixels_addr(const struct vkms_frame_info *frame_info, + int x, int y) +{ + size_t offset = pixel_offset(frame_info, x, y); + + return (u8 *)frame_info->map[0].vaddr + offset; +} + +static void *get_packed_src_addr(const struct vkms_frame_info *frame_info, int y) +{ + int x_src = frame_info->src.x1 >> 16; + int y_src = y - frame_info->dst.y1 + (frame_info->src.y1 >> 16); + + return packed_pixels_addr(frame_info, x_src, y_src); +} + +static void ARGB8888_to_argb_u16(struct line_buffer *stage_buffer, + const struct vkms_frame_info *frame_info, int y) +{ + struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; + u8 *src_pixels = get_packed_src_addr(frame_info, y); + int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), + stage_buffer->n_pixels); + + for (size_t x = 0; x < x_limit; x++, src_pixels += 4) { + /* + * 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_pixels[x].a = (u16)src_pixels[3] * 257; + out_pixels[x].r = (u16)src_pixels[2] * 257; + out_pixels[x].g = (u16)src_pixels[1] * 257; + out_pixels[x].b = (u16)src_pixels[0] * 257; + } +} + +static void XRGB8888_to_argb_u16(struct line_buffer *stage_buffer, + const struct vkms_frame_info *frame_info, int y) +{ + struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; + u8 *src_pixels = get_packed_src_addr(frame_info, y); + int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), + stage_buffer->n_pixels); + + for (size_t x = 0; x < x_limit; x++, src_pixels += 4) { + out_pixels[x].a = (u16)0xffff; + out_pixels[x].r = (u16)src_pixels[2] * 257; + out_pixels[x].g = (u16)src_pixels[1] * 257; + out_pixels[x].b = (u16)src_pixels[0] * 257; + } +} + +static void ARGB16161616_to_argb_u16(struct line_buffer *stage_buffer, + const struct vkms_frame_info *frame_info, + int y) +{ + struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; + u16 *src_pixels = get_packed_src_addr(frame_info, y); + int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), + stage_buffer->n_pixels); + + for (size_t x = 0; x < x_limit; x++, src_pixels += 4) { + out_pixels[x].a = le16_to_cpu(src_pixels[3]); + out_pixels[x].r = le16_to_cpu(src_pixels[2]); + out_pixels[x].g = le16_to_cpu(src_pixels[1]); + out_pixels[x].b = le16_to_cpu(src_pixels[0]); + } +} + +static void XRGB16161616_to_argb_u16(struct line_buffer *stage_buffer, + const struct vkms_frame_info *frame_info, + int y) +{ + struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; + u16 *src_pixels = get_packed_src_addr(frame_info, y); + int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), + stage_buffer->n_pixels); + + for (size_t x = 0; x < x_limit; x++, src_pixels += 4) { + out_pixels[x].a = (u16)0xffff; + out_pixels[x].r = le16_to_cpu(src_pixels[2]); + out_pixels[x].g = le16_to_cpu(src_pixels[1]); + out_pixels[x].b = le16_to_cpu(src_pixels[0]); + } +} + +static void RGB565_to_argb_u16(struct line_buffer *stage_buffer, + const struct vkms_frame_info *frame_info, int y) +{ + struct pixel_argb_u16 *out_pixels = stage_buffer->pixels; + u16 *src_pixels = get_packed_src_addr(frame_info, y); + int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), + stage_buffer->n_pixels); + + 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)); + + for (size_t x = 0; x < x_limit; x++, src_pixels++) { + u16 rgb_565 = le16_to_cpu(*src_pixels); + 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_pixels[x].a = (u16)0xffff; + out_pixels[x].r = drm_fixp2int(drm_fixp_mul(fp_r, fp_rb_ratio)); + out_pixels[x].g = drm_fixp2int(drm_fixp_mul(fp_g, fp_g_ratio)); + out_pixels[x].b = drm_fixp2int(drm_fixp_mul(fp_b, fp_rb_ratio)); + } +} + +/* + * The following functions take an line of argb_u16 pixels from the + * src_buffer, convert them to a specific format, and store them in the + * destination. + * + * They are used in the `compose_active_planes` to convert and store a line + * from the src_buffer to the writeback buffer. + */ +static void argb_u16_to_ARGB8888(struct vkms_frame_info *frame_info, + const struct line_buffer *src_buffer, int y) +{ + int x_dst = frame_info->dst.x1; + u8 *dst_pixels = packed_pixels_addr(frame_info, x_dst, 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 += 4) { + /* + * 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 + */ + dst_pixels[3] = DIV_ROUND_CLOSEST(in_pixels[x].a, 257); + dst_pixels[2] = DIV_ROUND_CLOSEST(in_pixels[x].r, 257); + dst_pixels[1] = DIV_ROUND_CLOSEST(in_pixels[x].g, 257); + dst_pixels[0] = DIV_ROUND_CLOSEST(in_pixels[x].b, 257); + } +} + +static void argb_u16_to_XRGB8888(struct vkms_frame_info *frame_info, + const struct line_buffer *src_buffer, int y) +{ + int x_dst = frame_info->dst.x1; + u8 *dst_pixels = packed_pixels_addr(frame_info, x_dst, 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 += 4) { + dst_pixels[3] = 0xff; + dst_pixels[2] = DIV_ROUND_CLOSEST(in_pixels[x].r, 257); + dst_pixels[1] = DIV_ROUND_CLOSEST(in_pixels[x].g, 257); + dst_pixels[0] = DIV_ROUND_CLOSEST(in_pixels[x].b, 257); + } +} + +static void argb_u16_to_ARGB16161616(struct vkms_frame_info *frame_info, + const struct line_buffer *src_buffer, int y) +{ + int x_dst = frame_info->dst.x1; + u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, 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 += 4) { + dst_pixels[3] = cpu_to_le16(in_pixels[x].a); + dst_pixels[2] = cpu_to_le16(in_pixels[x].r); + dst_pixels[1] = cpu_to_le16(in_pixels[x].g); + dst_pixels[0] = cpu_to_le16(in_pixels[x].b); + } +} + +static void argb_u16_to_XRGB16161616(struct vkms_frame_info *frame_info, + const struct line_buffer *src_buffer, int y) +{ + int x_dst = frame_info->dst.x1; + u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, 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 += 4) { + dst_pixels[3] = 0xffff; + dst_pixels[2] = cpu_to_le16(in_pixels[x].r); + dst_pixels[1] = cpu_to_le16(in_pixels[x].g); + dst_pixels[0] = cpu_to_le16(in_pixels[x].b); + } +} + +static void argb_u16_to_RGB565(struct vkms_frame_info *frame_info, + const struct line_buffer *src_buffer, int y) +{ + int x_dst = frame_info->dst.x1; + u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, 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); + + 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)); + + for (size_t x = 0; x < x_limit; x++, dst_pixels++) { + s64 fp_r = drm_int2fixp(in_pixels[x].r); + s64 fp_g = drm_int2fixp(in_pixels[x].g); + s64 fp_b = drm_int2fixp(in_pixels[x].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)); + + *dst_pixels = cpu_to_le16(r << 11 | g << 5 | b); + } +} + +void *get_frame_to_line_function(u32 format) +{ + switch (format) { + case DRM_FORMAT_ARGB8888: + return &ARGB8888_to_argb_u16; + case DRM_FORMAT_XRGB8888: + return &XRGB8888_to_argb_u16; + case DRM_FORMAT_ARGB16161616: + return &ARGB16161616_to_argb_u16; + case DRM_FORMAT_XRGB16161616: + return &XRGB16161616_to_argb_u16; + case DRM_FORMAT_RGB565: + return &RGB565_to_argb_u16; + default: + return NULL; + } +} + +void *get_line_to_frame_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_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: + return NULL; + } +} diff --git a/drivers/gpu/drm/vkms/vkms_formats.h b/drivers/gpu/drm/vkms/vkms_formats.h new file mode 100644 index 000000000000..43b7c1979018 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_formats.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _VKMS_FORMATS_H_ +#define _VKMS_FORMATS_H_ + +#include "vkms_drv.h" + +void *get_frame_to_line_function(u32 format); + +void *get_line_to_frame_function(u32 format); + +#endif /* _VKMS_FORMATS_H_ */ diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index d8eb674b49a6..c3a845220e10 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -7,37 +7,42 @@ #include <drm/drm_fourcc.h> #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> -#include <drm/drm_plane_helper.h> #include "vkms_drv.h" +#include "vkms_formats.h" static const u32 vkms_formats[] = { DRM_FORMAT_XRGB8888, + DRM_FORMAT_XRGB16161616, + DRM_FORMAT_RGB565 }; static const u32 vkms_plane_formats[] = { DRM_FORMAT_ARGB8888, - DRM_FORMAT_XRGB8888 + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XRGB16161616, + DRM_FORMAT_ARGB16161616, + DRM_FORMAT_RGB565 }; static struct drm_plane_state * vkms_plane_duplicate_state(struct drm_plane *plane) { struct vkms_plane_state *vkms_state; - struct vkms_composer *composer; + struct vkms_frame_info *frame_info; vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL); if (!vkms_state) return NULL; - composer = kzalloc(sizeof(*composer), GFP_KERNEL); - if (!composer) { - DRM_DEBUG_KMS("Couldn't allocate composer\n"); + frame_info = kzalloc(sizeof(*frame_info), GFP_KERNEL); + if (!frame_info) { + DRM_DEBUG_KMS("Couldn't allocate frame_info\n"); kfree(vkms_state); return NULL; } - vkms_state->composer = composer; + vkms_state->frame_info = frame_info; __drm_gem_duplicate_shadow_plane_state(plane, &vkms_state->base); @@ -50,16 +55,16 @@ static void vkms_plane_destroy_state(struct drm_plane *plane, struct vkms_plane_state *vkms_state = to_vkms_plane_state(old_state); struct drm_crtc *crtc = vkms_state->base.base.crtc; - if (crtc) { + if (crtc && vkms_state->frame_info->fb) { /* dropping the reference we acquired in * vkms_primary_plane_update() */ - if (drm_framebuffer_read_refcount(&vkms_state->composer->fb)) - drm_framebuffer_put(&vkms_state->composer->fb); + if (drm_framebuffer_read_refcount(vkms_state->frame_info->fb)) + drm_framebuffer_put(vkms_state->frame_info->fb); } - kfree(vkms_state->composer); - vkms_state->composer = NULL; + kfree(vkms_state->frame_info); + vkms_state->frame_info = NULL; __drm_gem_destroy_shadow_plane_state(&vkms_state->base); kfree(vkms_state); @@ -99,23 +104,26 @@ static void vkms_plane_atomic_update(struct drm_plane *plane, struct vkms_plane_state *vkms_plane_state; struct drm_shadow_plane_state *shadow_plane_state; struct drm_framebuffer *fb = new_state->fb; - struct vkms_composer *composer; + struct vkms_frame_info *frame_info; + u32 fmt; if (!new_state->crtc || !fb) return; + fmt = fb->format->format; vkms_plane_state = to_vkms_plane_state(new_state); shadow_plane_state = &vkms_plane_state->base; - composer = vkms_plane_state->composer; - memcpy(&composer->src, &new_state->src, sizeof(struct drm_rect)); - memcpy(&composer->dst, &new_state->dst, sizeof(struct drm_rect)); - memcpy(&composer->fb, fb, sizeof(struct drm_framebuffer)); - memcpy(&composer->map, &shadow_plane_state->data, sizeof(composer->map)); - drm_framebuffer_get(&composer->fb); - composer->offset = fb->offsets[0]; - composer->pitch = fb->pitches[0]; - composer->cpp = fb->format->cpp[0]; + frame_info = vkms_plane_state->frame_info; + memcpy(&frame_info->src, &new_state->src, sizeof(struct drm_rect)); + memcpy(&frame_info->dst, &new_state->dst, sizeof(struct drm_rect)); + frame_info->fb = fb; + memcpy(&frame_info->map, &shadow_plane_state->data, sizeof(frame_info->map)); + drm_framebuffer_get(frame_info->fb); + frame_info->offset = fb->offsets[0]; + frame_info->pitch = fb->pitches[0]; + frame_info->cpp = fb->format->cpp[0]; + vkms_plane_state->plane_read = get_frame_to_line_function(fmt); } static int vkms_plane_atomic_check(struct drm_plane *plane, @@ -139,8 +147,8 @@ static int vkms_plane_atomic_check(struct drm_plane *plane, can_position = true; ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, can_position, true); if (ret != 0) return ret; diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c index 3b3c1e757ab4..84a51cd281b9 100644 --- a/drivers/gpu/drm/vkms/vkms_writeback.c +++ b/drivers/gpu/drm/vkms/vkms_writeback.c @@ -12,9 +12,13 @@ #include <drm/drm_gem_shmem_helper.h> #include "vkms_drv.h" +#include "vkms_formats.h" static const u32 vkms_wb_formats[] = { DRM_FORMAT_XRGB8888, + DRM_FORMAT_XRGB16161616, + DRM_FORMAT_ARGB16161616, + DRM_FORMAT_RGB565 }; static const struct drm_connector_funcs vkms_wb_connector_funcs = { @@ -31,6 +35,7 @@ static int vkms_wb_encoder_atomic_check(struct drm_encoder *encoder, { struct drm_framebuffer *fb; const struct drm_display_mode *mode = &crtc_state->mode; + int ret; if (!conn_state->writeback_job || !conn_state->writeback_job->fb) return 0; @@ -42,11 +47,9 @@ static int vkms_wb_encoder_atomic_check(struct drm_encoder *encoder, return -EINVAL; } - if (fb->format->format != vkms_wb_formats[0]) { - DRM_DEBUG_KMS("Invalid pixel format %p4cc\n", - &fb->format->format); - return -EINVAL; - } + ret = drm_atomic_helper_check_wb_encoder_state(encoder, conn_state); + if (ret < 0) + return ret; return 0; } @@ -76,12 +79,15 @@ static int vkms_wb_prepare_job(struct drm_writeback_connector *wb_connector, if (!vkmsjob) return -ENOMEM; - ret = drm_gem_fb_vmap(job->fb, vkmsjob->map, vkmsjob->data); + ret = drm_gem_fb_vmap(job->fb, vkmsjob->wb_frame_info.map, vkmsjob->data); if (ret) { DRM_ERROR("vmap failed: %d\n", ret); goto err_kfree; } + vkmsjob->wb_frame_info.fb = job->fb; + drm_framebuffer_get(vkmsjob->wb_frame_info.fb); + job->priv = vkmsjob; return 0; @@ -100,7 +106,9 @@ static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector, if (!job->fb) return; - drm_gem_fb_vunmap(job->fb, vkmsjob->map); + drm_gem_fb_vunmap(job->fb, vkmsjob->wb_frame_info.map); + + drm_framebuffer_put(vkmsjob->wb_frame_info.fb); vkmsdev = drm_device_to_vkms_device(job->fb->dev); vkms_set_composer(&vkmsdev->output, false); @@ -117,17 +125,32 @@ static void vkms_wb_atomic_commit(struct drm_connector *conn, struct drm_writeback_connector *wb_conn = &output->wb_connector; struct drm_connector_state *conn_state = wb_conn->base.state; struct vkms_crtc_state *crtc_state = output->composer_state; + struct drm_framebuffer *fb = connector_state->writeback_job->fb; + u16 crtc_height = crtc_state->base.crtc->mode.vdisplay; + u16 crtc_width = crtc_state->base.crtc->mode.hdisplay; + struct vkms_writeback_job *active_wb; + struct vkms_frame_info *wb_frame_info; + u32 wb_format = fb->format->format; if (!conn_state) return; vkms_set_composer(&vkmsdev->output, true); + active_wb = conn_state->writeback_job->priv; + wb_frame_info = &active_wb->wb_frame_info; + spin_lock_irq(&output->composer_lock); - crtc_state->active_writeback = conn_state->writeback_job->priv; + crtc_state->active_writeback = active_wb; + wb_frame_info->offset = fb->offsets[0]; + wb_frame_info->pitch = fb->pitches[0]; + wb_frame_info->cpp = fb->format->cpp[0]; crtc_state->wb_pending = true; spin_unlock_irq(&output->composer_lock); drm_writeback_queue_job(wb_conn, connector_state); + active_wb->wb_write = get_line_to_frame_function(wb_format); + drm_rect_init(&wb_frame_info->src, 0, 0, crtc_width, crtc_height); + drm_rect_init(&wb_frame_info->dst, 0, 0, crtc_width, crtc_height); } static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = { |