1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include <linux/crc32.h> 4 5 #include <drm/drm_atomic.h> 6 #include <drm/drm_atomic_helper.h> 7 #include <drm/drm_fourcc.h> 8 #include <drm/drm_gem_framebuffer_helper.h> 9 #include <drm/drm_gem_shmem_helper.h> 10 #include <drm/drm_vblank.h> 11 12 #include "vkms_drv.h" 13 14 static u32 get_pixel_from_buffer(int x, int y, const u8 *buffer, 15 const struct vkms_composer *composer) 16 { 17 u32 pixel; 18 int src_offset = composer->offset + (y * composer->pitch) 19 + (x * composer->cpp); 20 21 pixel = *(u32 *)&buffer[src_offset]; 22 23 return pixel; 24 } 25 26 /** 27 * compute_crc - Compute CRC value on output frame 28 * 29 * @vaddr: address to final framebuffer 30 * @composer: framebuffer's metadata 31 * 32 * returns CRC value computed using crc32 on the visible portion of 33 * the final framebuffer at vaddr_out 34 */ 35 static uint32_t compute_crc(const u8 *vaddr, 36 const struct vkms_composer *composer) 37 { 38 int x, y; 39 u32 crc = 0, pixel = 0; 40 int x_src = composer->src.x1 >> 16; 41 int y_src = composer->src.y1 >> 16; 42 int h_src = drm_rect_height(&composer->src) >> 16; 43 int w_src = drm_rect_width(&composer->src) >> 16; 44 45 for (y = y_src; y < y_src + h_src; ++y) { 46 for (x = x_src; x < x_src + w_src; ++x) { 47 pixel = get_pixel_from_buffer(x, y, vaddr, composer); 48 crc = crc32_le(crc, (void *)&pixel, sizeof(u32)); 49 } 50 } 51 52 return crc; 53 } 54 55 static u8 blend_channel(u8 src, u8 dst, u8 alpha) 56 { 57 u32 pre_blend; 58 u8 new_color; 59 60 pre_blend = (src * 255 + dst * (255 - alpha)); 61 62 /* Faster div by 255 */ 63 new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8); 64 65 return new_color; 66 } 67 68 /** 69 * alpha_blend - alpha blending equation 70 * @argb_src: src pixel on premultiplied alpha mode 71 * @argb_dst: dst pixel completely opaque 72 * 73 * blend pixels using premultiplied blend formula. The current DRM assumption 74 * is that pixel color values have been already pre-multiplied with the alpha 75 * channel values. See more drm_plane_create_blend_mode_property(). Also, this 76 * formula assumes a completely opaque background. 77 */ 78 static void alpha_blend(const u8 *argb_src, u8 *argb_dst) 79 { 80 u8 alpha; 81 82 alpha = argb_src[3]; 83 argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha); 84 argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha); 85 argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha); 86 } 87 88 /** 89 * x_blend - blending equation that ignores the pixel alpha 90 * 91 * overwrites RGB color value from src pixel to dst pixel. 92 */ 93 static void x_blend(const u8 *xrgb_src, u8 *xrgb_dst) 94 { 95 memcpy(xrgb_dst, xrgb_src, sizeof(u8) * 3); 96 } 97 98 /** 99 * blend - blend value at vaddr_src with value at vaddr_dst 100 * @vaddr_dst: destination address 101 * @vaddr_src: source address 102 * @dst_composer: destination framebuffer's metadata 103 * @src_composer: source framebuffer's metadata 104 * @pixel_blend: blending equation based on plane format 105 * 106 * Blend the vaddr_src value with the vaddr_dst value using a pixel blend 107 * equation according to the supported plane formats DRM_FORMAT_(A/XRGB8888) 108 * and clearing alpha channel to an completely opaque background. This function 109 * uses buffer's metadata to locate the new composite values at vaddr_dst. 110 * 111 * TODO: completely clear the primary plane (a = 0xff) before starting to blend 112 * pixel color values 113 */ 114 static void blend(void *vaddr_dst, void *vaddr_src, 115 struct vkms_composer *dst_composer, 116 struct vkms_composer *src_composer, 117 void (*pixel_blend)(const u8 *, u8 *)) 118 { 119 int i, j, j_dst, i_dst; 120 int offset_src, offset_dst; 121 u8 *pixel_dst, *pixel_src; 122 123 int x_src = src_composer->src.x1 >> 16; 124 int y_src = src_composer->src.y1 >> 16; 125 126 int x_dst = src_composer->dst.x1; 127 int y_dst = src_composer->dst.y1; 128 int h_dst = drm_rect_height(&src_composer->dst); 129 int w_dst = drm_rect_width(&src_composer->dst); 130 131 int y_limit = y_src + h_dst; 132 int x_limit = x_src + w_dst; 133 134 for (i = y_src, i_dst = y_dst; i < y_limit; ++i) { 135 for (j = x_src, j_dst = x_dst; j < x_limit; ++j) { 136 offset_dst = dst_composer->offset 137 + (i_dst * dst_composer->pitch) 138 + (j_dst++ * dst_composer->cpp); 139 offset_src = src_composer->offset 140 + (i * src_composer->pitch) 141 + (j * src_composer->cpp); 142 143 pixel_src = (u8 *)(vaddr_src + offset_src); 144 pixel_dst = (u8 *)(vaddr_dst + offset_dst); 145 pixel_blend(pixel_src, pixel_dst); 146 /* clearing alpha channel (0xff)*/ 147 pixel_dst[3] = 0xff; 148 } 149 i_dst++; 150 } 151 } 152 153 static void compose_plane(struct vkms_composer *primary_composer, 154 struct vkms_composer *plane_composer, 155 void *vaddr_out) 156 { 157 struct drm_gem_object *plane_obj; 158 struct drm_gem_shmem_object *plane_shmem_obj; 159 struct drm_framebuffer *fb = &plane_composer->fb; 160 void (*pixel_blend)(const u8 *p_src, u8 *p_dst); 161 162 plane_obj = drm_gem_fb_get_obj(&plane_composer->fb, 0); 163 plane_shmem_obj = to_drm_gem_shmem_obj(plane_obj); 164 165 if (WARN_ON(!plane_shmem_obj->vaddr)) 166 return; 167 168 if (fb->format->format == DRM_FORMAT_ARGB8888) 169 pixel_blend = &alpha_blend; 170 else 171 pixel_blend = &x_blend; 172 173 blend(vaddr_out, plane_shmem_obj->vaddr, primary_composer, 174 plane_composer, pixel_blend); 175 } 176 177 static int compose_active_planes(void **vaddr_out, 178 struct vkms_composer *primary_composer, 179 struct vkms_crtc_state *crtc_state) 180 { 181 struct drm_framebuffer *fb = &primary_composer->fb; 182 struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0); 183 struct drm_gem_shmem_object *shmem_obj = to_drm_gem_shmem_obj(gem_obj); 184 int i; 185 186 if (!*vaddr_out) { 187 *vaddr_out = kzalloc(shmem_obj->base.size, GFP_KERNEL); 188 if (!*vaddr_out) { 189 DRM_ERROR("Cannot allocate memory for output frame."); 190 return -ENOMEM; 191 } 192 } 193 194 if (WARN_ON(!shmem_obj->vaddr)) 195 return -EINVAL; 196 197 memcpy(*vaddr_out, shmem_obj->vaddr, shmem_obj->base.size); 198 199 /* If there are other planes besides primary, we consider the active 200 * planes should be in z-order and compose them associatively: 201 * ((primary <- overlay) <- cursor) 202 */ 203 for (i = 1; i < crtc_state->num_active_planes; i++) 204 compose_plane(primary_composer, 205 crtc_state->active_planes[i]->composer, 206 *vaddr_out); 207 208 return 0; 209 } 210 211 /** 212 * vkms_composer_worker - ordered work_struct to compute CRC 213 * 214 * @work: work_struct 215 * 216 * Work handler for composing and computing CRCs. work_struct scheduled in 217 * an ordered workqueue that's periodically scheduled to run by 218 * _vblank_handle() and flushed at vkms_atomic_crtc_destroy_state(). 219 */ 220 void vkms_composer_worker(struct work_struct *work) 221 { 222 struct vkms_crtc_state *crtc_state = container_of(work, 223 struct vkms_crtc_state, 224 composer_work); 225 struct drm_crtc *crtc = crtc_state->base.crtc; 226 struct vkms_output *out = drm_crtc_to_vkms_output(crtc); 227 struct vkms_composer *primary_composer = NULL; 228 struct vkms_plane_state *act_plane = NULL; 229 bool crc_pending, wb_pending; 230 void *vaddr_out = NULL; 231 u32 crc32 = 0; 232 u64 frame_start, frame_end; 233 int ret; 234 235 spin_lock_irq(&out->composer_lock); 236 frame_start = crtc_state->frame_start; 237 frame_end = crtc_state->frame_end; 238 crc_pending = crtc_state->crc_pending; 239 wb_pending = crtc_state->wb_pending; 240 crtc_state->frame_start = 0; 241 crtc_state->frame_end = 0; 242 crtc_state->crc_pending = false; 243 spin_unlock_irq(&out->composer_lock); 244 245 /* 246 * We raced with the vblank hrtimer and previous work already computed 247 * the crc, nothing to do. 248 */ 249 if (!crc_pending) 250 return; 251 252 if (crtc_state->num_active_planes >= 1) { 253 act_plane = crtc_state->active_planes[0]; 254 if (act_plane->base.plane->type == DRM_PLANE_TYPE_PRIMARY) 255 primary_composer = act_plane->composer; 256 } 257 258 if (!primary_composer) 259 return; 260 261 if (wb_pending) 262 vaddr_out = crtc_state->active_writeback; 263 264 ret = compose_active_planes(&vaddr_out, primary_composer, 265 crtc_state); 266 if (ret) { 267 if (ret == -EINVAL && !wb_pending) 268 kfree(vaddr_out); 269 return; 270 } 271 272 crc32 = compute_crc(vaddr_out, primary_composer); 273 274 if (wb_pending) { 275 drm_writeback_signal_completion(&out->wb_connector, 0); 276 spin_lock_irq(&out->composer_lock); 277 crtc_state->wb_pending = false; 278 spin_unlock_irq(&out->composer_lock); 279 } else { 280 kfree(vaddr_out); 281 } 282 283 /* 284 * The worker can fall behind the vblank hrtimer, make sure we catch up. 285 */ 286 while (frame_start <= frame_end) 287 drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32); 288 } 289 290 static const char * const pipe_crc_sources[] = {"auto"}; 291 292 const char *const *vkms_get_crc_sources(struct drm_crtc *crtc, 293 size_t *count) 294 { 295 *count = ARRAY_SIZE(pipe_crc_sources); 296 return pipe_crc_sources; 297 } 298 299 static int vkms_crc_parse_source(const char *src_name, bool *enabled) 300 { 301 int ret = 0; 302 303 if (!src_name) { 304 *enabled = false; 305 } else if (strcmp(src_name, "auto") == 0) { 306 *enabled = true; 307 } else { 308 *enabled = false; 309 ret = -EINVAL; 310 } 311 312 return ret; 313 } 314 315 int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name, 316 size_t *values_cnt) 317 { 318 bool enabled; 319 320 if (vkms_crc_parse_source(src_name, &enabled) < 0) { 321 DRM_DEBUG_DRIVER("unknown source %s\n", src_name); 322 return -EINVAL; 323 } 324 325 *values_cnt = 1; 326 327 return 0; 328 } 329 330 void vkms_set_composer(struct vkms_output *out, bool enabled) 331 { 332 bool old_enabled; 333 334 if (enabled) 335 drm_crtc_vblank_get(&out->crtc); 336 337 spin_lock_irq(&out->lock); 338 old_enabled = out->composer_enabled; 339 out->composer_enabled = enabled; 340 spin_unlock_irq(&out->lock); 341 342 if (old_enabled) 343 drm_crtc_vblank_put(&out->crtc); 344 } 345 346 int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name) 347 { 348 struct vkms_output *out = drm_crtc_to_vkms_output(crtc); 349 bool enabled = false; 350 int ret = 0; 351 352 ret = vkms_crc_parse_source(src_name, &enabled); 353 354 vkms_set_composer(out, enabled); 355 356 return ret; 357 } 358