Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 #include <linux/crc32.h>
0004 
0005 #include <drm/drm_atomic.h>
0006 #include <drm/drm_atomic_helper.h>
0007 #include <drm/drm_fourcc.h>
0008 #include <drm/drm_gem_framebuffer_helper.h>
0009 #include <drm/drm_vblank.h>
0010 
0011 #include "vkms_drv.h"
0012 
0013 static u32 get_pixel_from_buffer(int x, int y, const u8 *buffer,
0014                  const struct vkms_composer *composer)
0015 {
0016     u32 pixel;
0017     int src_offset = composer->offset + (y * composer->pitch)
0018                       + (x * composer->cpp);
0019 
0020     pixel = *(u32 *)&buffer[src_offset];
0021 
0022     return pixel;
0023 }
0024 
0025 /**
0026  * compute_crc - Compute CRC value on output frame
0027  *
0028  * @vaddr: address to final framebuffer
0029  * @composer: framebuffer's metadata
0030  *
0031  * returns CRC value computed using crc32 on the visible portion of
0032  * the final framebuffer at vaddr_out
0033  */
0034 static uint32_t compute_crc(const u8 *vaddr,
0035                 const struct vkms_composer *composer)
0036 {
0037     int x, y;
0038     u32 crc = 0, pixel = 0;
0039     int x_src = composer->src.x1 >> 16;
0040     int y_src = composer->src.y1 >> 16;
0041     int h_src = drm_rect_height(&composer->src) >> 16;
0042     int w_src = drm_rect_width(&composer->src) >> 16;
0043 
0044     for (y = y_src; y < y_src + h_src; ++y) {
0045         for (x = x_src; x < x_src + w_src; ++x) {
0046             pixel = get_pixel_from_buffer(x, y, vaddr, composer);
0047             crc = crc32_le(crc, (void *)&pixel, sizeof(u32));
0048         }
0049     }
0050 
0051     return crc;
0052 }
0053 
0054 static u8 blend_channel(u8 src, u8 dst, u8 alpha)
0055 {
0056     u32 pre_blend;
0057     u8 new_color;
0058 
0059     pre_blend = (src * 255 + dst * (255 - alpha));
0060 
0061     /* Faster div by 255 */
0062     new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8);
0063 
0064     return new_color;
0065 }
0066 
0067 /**
0068  * alpha_blend - alpha blending equation
0069  * @argb_src: src pixel on premultiplied alpha mode
0070  * @argb_dst: dst pixel completely opaque
0071  *
0072  * blend pixels using premultiplied blend formula. The current DRM assumption
0073  * is that pixel color values have been already pre-multiplied with the alpha
0074  * channel values. See more drm_plane_create_blend_mode_property(). Also, this
0075  * formula assumes a completely opaque background.
0076  */
0077 static void alpha_blend(const u8 *argb_src, u8 *argb_dst)
0078 {
0079     u8 alpha;
0080 
0081     alpha = argb_src[3];
0082     argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha);
0083     argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha);
0084     argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha);
0085 }
0086 
0087 /**
0088  * x_blend - blending equation that ignores the pixel alpha
0089  *
0090  * overwrites RGB color value from src pixel to dst pixel.
0091  */
0092 static void x_blend(const u8 *xrgb_src, u8 *xrgb_dst)
0093 {
0094     memcpy(xrgb_dst, xrgb_src, sizeof(u8) * 3);
0095 }
0096 
0097 /**
0098  * blend - blend value at vaddr_src with value at vaddr_dst
0099  * @vaddr_dst: destination address
0100  * @vaddr_src: source address
0101  * @dst_composer: destination framebuffer's metadata
0102  * @src_composer: source framebuffer's metadata
0103  * @pixel_blend: blending equation based on plane format
0104  *
0105  * Blend the vaddr_src value with the vaddr_dst value using a pixel blend
0106  * equation according to the supported plane formats DRM_FORMAT_(A/XRGB8888)
0107  * and clearing alpha channel to an completely opaque background. This function
0108  * uses buffer's metadata to locate the new composite values at vaddr_dst.
0109  *
0110  * TODO: completely clear the primary plane (a = 0xff) before starting to blend
0111  * pixel color values
0112  */
0113 static void blend(void *vaddr_dst, void *vaddr_src,
0114           struct vkms_composer *dst_composer,
0115           struct vkms_composer *src_composer,
0116           void (*pixel_blend)(const u8 *, u8 *))
0117 {
0118     int i, j, j_dst, i_dst;
0119     int offset_src, offset_dst;
0120     u8 *pixel_dst, *pixel_src;
0121 
0122     int x_src = src_composer->src.x1 >> 16;
0123     int y_src = src_composer->src.y1 >> 16;
0124 
0125     int x_dst = src_composer->dst.x1;
0126     int y_dst = src_composer->dst.y1;
0127     int h_dst = drm_rect_height(&src_composer->dst);
0128     int w_dst = drm_rect_width(&src_composer->dst);
0129 
0130     int y_limit = y_src + h_dst;
0131     int x_limit = x_src + w_dst;
0132 
0133     for (i = y_src, i_dst = y_dst; i < y_limit; ++i) {
0134         for (j = x_src, j_dst = x_dst; j < x_limit; ++j) {
0135             offset_dst = dst_composer->offset
0136                      + (i_dst * dst_composer->pitch)
0137                      + (j_dst++ * dst_composer->cpp);
0138             offset_src = src_composer->offset
0139                      + (i * src_composer->pitch)
0140                      + (j * src_composer->cpp);
0141 
0142             pixel_src = (u8 *)(vaddr_src + offset_src);
0143             pixel_dst = (u8 *)(vaddr_dst + offset_dst);
0144             pixel_blend(pixel_src, pixel_dst);
0145             /* clearing alpha channel (0xff)*/
0146             pixel_dst[3] = 0xff;
0147         }
0148         i_dst++;
0149     }
0150 }
0151 
0152 static void compose_plane(struct vkms_composer *primary_composer,
0153               struct vkms_composer *plane_composer,
0154               void *vaddr_out)
0155 {
0156     struct drm_framebuffer *fb = &plane_composer->fb;
0157     void *vaddr;
0158     void (*pixel_blend)(const u8 *p_src, u8 *p_dst);
0159 
0160     if (WARN_ON(iosys_map_is_null(&plane_composer->map[0])))
0161         return;
0162 
0163     vaddr = plane_composer->map[0].vaddr;
0164 
0165     if (fb->format->format == DRM_FORMAT_ARGB8888)
0166         pixel_blend = &alpha_blend;
0167     else
0168         pixel_blend = &x_blend;
0169 
0170     blend(vaddr_out, vaddr, primary_composer, plane_composer, pixel_blend);
0171 }
0172 
0173 static int compose_active_planes(void **vaddr_out,
0174                  struct vkms_composer *primary_composer,
0175                  struct vkms_crtc_state *crtc_state)
0176 {
0177     struct drm_framebuffer *fb = &primary_composer->fb;
0178     struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0);
0179     const void *vaddr;
0180     int i;
0181 
0182     if (!*vaddr_out) {
0183         *vaddr_out = kvzalloc(gem_obj->size, GFP_KERNEL);
0184         if (!*vaddr_out) {
0185             DRM_ERROR("Cannot allocate memory for output frame.");
0186             return -ENOMEM;
0187         }
0188     }
0189 
0190     if (WARN_ON(iosys_map_is_null(&primary_composer->map[0])))
0191         return -EINVAL;
0192 
0193     vaddr = primary_composer->map[0].vaddr;
0194 
0195     memcpy(*vaddr_out, vaddr, gem_obj->size);
0196 
0197     /* If there are other planes besides primary, we consider the active
0198      * planes should be in z-order and compose them associatively:
0199      * ((primary <- overlay) <- cursor)
0200      */
0201     for (i = 1; i < crtc_state->num_active_planes; i++)
0202         compose_plane(primary_composer,
0203                   crtc_state->active_planes[i]->composer,
0204                   *vaddr_out);
0205 
0206     return 0;
0207 }
0208 
0209 /**
0210  * vkms_composer_worker - ordered work_struct to compute CRC
0211  *
0212  * @work: work_struct
0213  *
0214  * Work handler for composing and computing CRCs. work_struct scheduled in
0215  * an ordered workqueue that's periodically scheduled to run by
0216  * vkms_vblank_simulate() and flushed at vkms_atomic_commit_tail().
0217  */
0218 void vkms_composer_worker(struct work_struct *work)
0219 {
0220     struct vkms_crtc_state *crtc_state = container_of(work,
0221                         struct vkms_crtc_state,
0222                         composer_work);
0223     struct drm_crtc *crtc = crtc_state->base.crtc;
0224     struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
0225     struct vkms_composer *primary_composer = NULL;
0226     struct vkms_plane_state *act_plane = NULL;
0227     bool crc_pending, wb_pending;
0228     void *vaddr_out = NULL;
0229     u32 crc32 = 0;
0230     u64 frame_start, frame_end;
0231     int ret;
0232 
0233     spin_lock_irq(&out->composer_lock);
0234     frame_start = crtc_state->frame_start;
0235     frame_end = crtc_state->frame_end;
0236     crc_pending = crtc_state->crc_pending;
0237     wb_pending = crtc_state->wb_pending;
0238     crtc_state->frame_start = 0;
0239     crtc_state->frame_end = 0;
0240     crtc_state->crc_pending = false;
0241     spin_unlock_irq(&out->composer_lock);
0242 
0243     /*
0244      * We raced with the vblank hrtimer and previous work already computed
0245      * the crc, nothing to do.
0246      */
0247     if (!crc_pending)
0248         return;
0249 
0250     if (crtc_state->num_active_planes >= 1) {
0251         act_plane = crtc_state->active_planes[0];
0252         if (act_plane->base.base.plane->type == DRM_PLANE_TYPE_PRIMARY)
0253             primary_composer = act_plane->composer;
0254     }
0255 
0256     if (!primary_composer)
0257         return;
0258 
0259     if (wb_pending)
0260         vaddr_out = crtc_state->active_writeback->data[0].vaddr;
0261 
0262     ret = compose_active_planes(&vaddr_out, primary_composer,
0263                     crtc_state);
0264     if (ret) {
0265         if (ret == -EINVAL && !wb_pending)
0266             kvfree(vaddr_out);
0267         return;
0268     }
0269 
0270     crc32 = compute_crc(vaddr_out, primary_composer);
0271 
0272     if (wb_pending) {
0273         drm_writeback_signal_completion(&out->wb_connector, 0);
0274         spin_lock_irq(&out->composer_lock);
0275         crtc_state->wb_pending = false;
0276         spin_unlock_irq(&out->composer_lock);
0277     } else {
0278         kvfree(vaddr_out);
0279     }
0280 
0281     /*
0282      * The worker can fall behind the vblank hrtimer, make sure we catch up.
0283      */
0284     while (frame_start <= frame_end)
0285         drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32);
0286 }
0287 
0288 static const char * const pipe_crc_sources[] = {"auto"};
0289 
0290 const char *const *vkms_get_crc_sources(struct drm_crtc *crtc,
0291                     size_t *count)
0292 {
0293     *count = ARRAY_SIZE(pipe_crc_sources);
0294     return pipe_crc_sources;
0295 }
0296 
0297 static int vkms_crc_parse_source(const char *src_name, bool *enabled)
0298 {
0299     int ret = 0;
0300 
0301     if (!src_name) {
0302         *enabled = false;
0303     } else if (strcmp(src_name, "auto") == 0) {
0304         *enabled = true;
0305     } else {
0306         *enabled = false;
0307         ret = -EINVAL;
0308     }
0309 
0310     return ret;
0311 }
0312 
0313 int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
0314                size_t *values_cnt)
0315 {
0316     bool enabled;
0317 
0318     if (vkms_crc_parse_source(src_name, &enabled) < 0) {
0319         DRM_DEBUG_DRIVER("unknown source %s\n", src_name);
0320         return -EINVAL;
0321     }
0322 
0323     *values_cnt = 1;
0324 
0325     return 0;
0326 }
0327 
0328 void vkms_set_composer(struct vkms_output *out, bool enabled)
0329 {
0330     bool old_enabled;
0331 
0332     if (enabled)
0333         drm_crtc_vblank_get(&out->crtc);
0334 
0335     spin_lock_irq(&out->lock);
0336     old_enabled = out->composer_enabled;
0337     out->composer_enabled = enabled;
0338     spin_unlock_irq(&out->lock);
0339 
0340     if (old_enabled)
0341         drm_crtc_vblank_put(&out->crtc);
0342 }
0343 
0344 int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name)
0345 {
0346     struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
0347     bool enabled = false;
0348     int ret = 0;
0349 
0350     ret = vkms_crc_parse_source(src_name, &enabled);
0351 
0352     vkms_set_composer(out, enabled);
0353 
0354     return ret;
0355 }