Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.0-rc5 262 lines 6.9 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2#include "vkms_drv.h" 3#include <linux/crc32.h> 4#include <drm/drm_atomic.h> 5#include <drm/drm_atomic_helper.h> 6#include <drm/drm_gem_framebuffer_helper.h> 7 8/** 9 * compute_crc - Compute CRC value on output frame 10 * 11 * @vaddr_out: address to final framebuffer 12 * @crc_out: framebuffer's metadata 13 * 14 * returns CRC value computed using crc32 on the visible portion of 15 * the final framebuffer at vaddr_out 16 */ 17static uint32_t compute_crc(void *vaddr_out, struct vkms_crc_data *crc_out) 18{ 19 int i, j, src_offset; 20 int x_src = crc_out->src.x1 >> 16; 21 int y_src = crc_out->src.y1 >> 16; 22 int h_src = drm_rect_height(&crc_out->src) >> 16; 23 int w_src = drm_rect_width(&crc_out->src) >> 16; 24 u32 crc = 0; 25 26 for (i = y_src; i < y_src + h_src; ++i) { 27 for (j = x_src; j < x_src + w_src; ++j) { 28 src_offset = crc_out->offset 29 + (i * crc_out->pitch) 30 + (j * crc_out->cpp); 31 /* XRGB format ignores Alpha channel */ 32 memset(vaddr_out + src_offset + 24, 0, 8); 33 crc = crc32_le(crc, vaddr_out + src_offset, 34 sizeof(u32)); 35 } 36 } 37 38 return crc; 39} 40 41/** 42 * blend - belnd value at vaddr_src with value at vaddr_dst 43 * @vaddr_dst: destination address 44 * @vaddr_src: source address 45 * @crc_dst: destination framebuffer's metadata 46 * @crc_src: source framebuffer's metadata 47 * 48 * Blend value at vaddr_src with value at vaddr_dst. 49 * Currently, this function write value at vaddr_src on value 50 * at vaddr_dst using buffer's metadata to locate the new values 51 * from vaddr_src and their distenation at vaddr_dst. 52 * 53 * Todo: Use the alpha value to blend vaddr_src with vaddr_dst 54 * instead of overwriting it. 55 */ 56static void blend(void *vaddr_dst, void *vaddr_src, 57 struct vkms_crc_data *crc_dst, 58 struct vkms_crc_data *crc_src) 59{ 60 int i, j, j_dst, i_dst; 61 int offset_src, offset_dst; 62 63 int x_src = crc_src->src.x1 >> 16; 64 int y_src = crc_src->src.y1 >> 16; 65 66 int x_dst = crc_src->dst.x1; 67 int y_dst = crc_src->dst.y1; 68 int h_dst = drm_rect_height(&crc_src->dst); 69 int w_dst = drm_rect_width(&crc_src->dst); 70 71 int y_limit = y_src + h_dst; 72 int x_limit = x_src + w_dst; 73 74 for (i = y_src, i_dst = y_dst; i < y_limit; ++i) { 75 for (j = x_src, j_dst = x_dst; j < x_limit; ++j) { 76 offset_dst = crc_dst->offset 77 + (i_dst * crc_dst->pitch) 78 + (j_dst++ * crc_dst->cpp); 79 offset_src = crc_src->offset 80 + (i * crc_src->pitch) 81 + (j * crc_src->cpp); 82 83 memcpy(vaddr_dst + offset_dst, 84 vaddr_src + offset_src, sizeof(u32)); 85 } 86 i_dst++; 87 } 88} 89 90static void compose_cursor(struct vkms_crc_data *cursor_crc, 91 struct vkms_crc_data *primary_crc, void *vaddr_out) 92{ 93 struct drm_gem_object *cursor_obj; 94 struct vkms_gem_object *cursor_vkms_obj; 95 96 cursor_obj = drm_gem_fb_get_obj(&cursor_crc->fb, 0); 97 cursor_vkms_obj = drm_gem_to_vkms_gem(cursor_obj); 98 99 mutex_lock(&cursor_vkms_obj->pages_lock); 100 if (!cursor_vkms_obj->vaddr) { 101 DRM_WARN("cursor plane vaddr is NULL"); 102 goto out; 103 } 104 105 blend(vaddr_out, cursor_vkms_obj->vaddr, primary_crc, cursor_crc); 106 107out: 108 mutex_unlock(&cursor_vkms_obj->pages_lock); 109} 110 111static uint32_t _vkms_get_crc(struct vkms_crc_data *primary_crc, 112 struct vkms_crc_data *cursor_crc) 113{ 114 struct drm_framebuffer *fb = &primary_crc->fb; 115 struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0); 116 struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(gem_obj); 117 void *vaddr_out = kzalloc(vkms_obj->gem.size, GFP_KERNEL); 118 u32 crc = 0; 119 120 if (!vaddr_out) { 121 DRM_ERROR("Failed to allocate memory for output frame."); 122 return 0; 123 } 124 125 mutex_lock(&vkms_obj->pages_lock); 126 if (WARN_ON(!vkms_obj->vaddr)) { 127 mutex_unlock(&vkms_obj->pages_lock); 128 kfree(vaddr_out); 129 return crc; 130 } 131 132 memcpy(vaddr_out, vkms_obj->vaddr, vkms_obj->gem.size); 133 mutex_unlock(&vkms_obj->pages_lock); 134 135 if (cursor_crc) 136 compose_cursor(cursor_crc, primary_crc, vaddr_out); 137 138 crc = compute_crc(vaddr_out, primary_crc); 139 140 kfree(vaddr_out); 141 142 return crc; 143} 144 145/** 146 * vkms_crc_work_handle - ordered work_struct to compute CRC 147 * 148 * @work: work_struct 149 * 150 * Work handler for computing CRCs. work_struct scheduled in 151 * an ordered workqueue that's periodically scheduled to run by 152 * _vblank_handle() and flushed at vkms_atomic_crtc_destroy_state(). 153 */ 154void vkms_crc_work_handle(struct work_struct *work) 155{ 156 struct vkms_crtc_state *crtc_state = container_of(work, 157 struct vkms_crtc_state, 158 crc_work); 159 struct drm_crtc *crtc = crtc_state->base.crtc; 160 struct vkms_output *out = drm_crtc_to_vkms_output(crtc); 161 struct vkms_device *vdev = container_of(out, struct vkms_device, 162 output); 163 struct vkms_crc_data *primary_crc = NULL; 164 struct vkms_crc_data *cursor_crc = NULL; 165 struct drm_plane *plane; 166 u32 crc32 = 0; 167 u64 frame_start, frame_end; 168 unsigned long flags; 169 170 spin_lock_irqsave(&out->state_lock, flags); 171 frame_start = crtc_state->frame_start; 172 frame_end = crtc_state->frame_end; 173 spin_unlock_irqrestore(&out->state_lock, flags); 174 175 /* _vblank_handle() hasn't updated frame_start yet */ 176 if (!frame_start || frame_start == frame_end) 177 goto out; 178 179 drm_for_each_plane(plane, &vdev->drm) { 180 struct vkms_plane_state *vplane_state; 181 struct vkms_crc_data *crc_data; 182 183 vplane_state = to_vkms_plane_state(plane->state); 184 crc_data = vplane_state->crc_data; 185 186 if (drm_framebuffer_read_refcount(&crc_data->fb) == 0) 187 continue; 188 189 if (plane->type == DRM_PLANE_TYPE_PRIMARY) 190 primary_crc = crc_data; 191 else 192 cursor_crc = crc_data; 193 } 194 195 if (primary_crc) 196 crc32 = _vkms_get_crc(primary_crc, cursor_crc); 197 198 frame_end = drm_crtc_accurate_vblank_count(crtc); 199 200 /* queue_work can fail to schedule crc_work; add crc for 201 * missing frames 202 */ 203 while (frame_start <= frame_end) 204 drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32); 205 206out: 207 /* to avoid using the same value for frame number again */ 208 spin_lock_irqsave(&out->state_lock, flags); 209 crtc_state->frame_end = frame_end; 210 crtc_state->frame_start = 0; 211 spin_unlock_irqrestore(&out->state_lock, flags); 212} 213 214static int vkms_crc_parse_source(const char *src_name, bool *enabled) 215{ 216 int ret = 0; 217 218 if (!src_name) { 219 *enabled = false; 220 } else if (strcmp(src_name, "auto") == 0) { 221 *enabled = true; 222 } else { 223 *enabled = false; 224 ret = -EINVAL; 225 } 226 227 return ret; 228} 229 230int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name, 231 size_t *values_cnt) 232{ 233 bool enabled; 234 235 if (vkms_crc_parse_source(src_name, &enabled) < 0) { 236 DRM_DEBUG_DRIVER("unknown source %s\n", src_name); 237 return -EINVAL; 238 } 239 240 *values_cnt = 1; 241 242 return 0; 243} 244 245int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name) 246{ 247 struct vkms_output *out = drm_crtc_to_vkms_output(crtc); 248 bool enabled = false; 249 unsigned long flags; 250 int ret = 0; 251 252 ret = vkms_crc_parse_source(src_name, &enabled); 253 254 /* make sure nothing is scheduled on crtc workq */ 255 flush_workqueue(out->crc_workq); 256 257 spin_lock_irqsave(&out->lock, flags); 258 out->crc_enabled = enabled; 259 spin_unlock_irqrestore(&out->lock, flags); 260 261 return ret; 262}