1a4e7e98eSRodrigo Siqueira // SPDX-License-Identifier: GPL-2.0+ 2a4e7e98eSRodrigo Siqueira 3a4e7e98eSRodrigo Siqueira #include <linux/crc32.h> 4ce672a1bSSam Ravnborg 5a4e7e98eSRodrigo Siqueira #include <drm/drm_atomic.h> 6a4e7e98eSRodrigo Siqueira #include <drm/drm_atomic_helper.h> 732a1648aSMelissa Wen #include <drm/drm_fourcc.h> 8a4e7e98eSRodrigo Siqueira #include <drm/drm_gem_framebuffer_helper.h> 9ce672a1bSSam Ravnborg #include <drm/drm_vblank.h> 10ce672a1bSSam Ravnborg 11ce672a1bSSam Ravnborg #include "vkms_drv.h" 12a4e7e98eSRodrigo Siqueira 1360cc2021SRodrigo Siqueira static u32 get_pixel_from_buffer(int x, int y, const u8 *buffer, 14*1645e7b9SIgor Torrente const struct vkms_frame_info *frame_info) 1560cc2021SRodrigo Siqueira { 1660cc2021SRodrigo Siqueira u32 pixel; 17*1645e7b9SIgor Torrente int src_offset = frame_info->offset + (y * frame_info->pitch) 18*1645e7b9SIgor Torrente + (x * frame_info->cpp); 1960cc2021SRodrigo Siqueira 2060cc2021SRodrigo Siqueira pixel = *(u32 *)&buffer[src_offset]; 2160cc2021SRodrigo Siqueira 2260cc2021SRodrigo Siqueira return pixel; 2360cc2021SRodrigo Siqueira } 2460cc2021SRodrigo Siqueira 25a4e7e98eSRodrigo Siqueira /** 26a4e7e98eSRodrigo Siqueira * compute_crc - Compute CRC value on output frame 27a4e7e98eSRodrigo Siqueira * 2860cc2021SRodrigo Siqueira * @vaddr: address to final framebuffer 29*1645e7b9SIgor Torrente * @frame_info: framebuffer's metadata 30a4e7e98eSRodrigo Siqueira * 31a4e7e98eSRodrigo Siqueira * returns CRC value computed using crc32 on the visible portion of 32a4e7e98eSRodrigo Siqueira * the final framebuffer at vaddr_out 33a4e7e98eSRodrigo Siqueira */ 3460cc2021SRodrigo Siqueira static uint32_t compute_crc(const u8 *vaddr, 35*1645e7b9SIgor Torrente const struct vkms_frame_info *frame_info) 36a4e7e98eSRodrigo Siqueira { 3760cc2021SRodrigo Siqueira int x, y; 3860cc2021SRodrigo Siqueira u32 crc = 0, pixel = 0; 39*1645e7b9SIgor Torrente int x_src = frame_info->src.x1 >> 16; 40*1645e7b9SIgor Torrente int y_src = frame_info->src.y1 >> 16; 41*1645e7b9SIgor Torrente int h_src = drm_rect_height(&frame_info->src) >> 16; 42*1645e7b9SIgor Torrente int w_src = drm_rect_width(&frame_info->src) >> 16; 43a4e7e98eSRodrigo Siqueira 4460cc2021SRodrigo Siqueira for (y = y_src; y < y_src + h_src; ++y) { 4560cc2021SRodrigo Siqueira for (x = x_src; x < x_src + w_src; ++x) { 46*1645e7b9SIgor Torrente pixel = get_pixel_from_buffer(x, y, vaddr, frame_info); 4760cc2021SRodrigo Siqueira crc = crc32_le(crc, (void *)&pixel, sizeof(u32)); 48a4e7e98eSRodrigo Siqueira } 49a4e7e98eSRodrigo Siqueira } 50a4e7e98eSRodrigo Siqueira 51a4e7e98eSRodrigo Siqueira return crc; 52a4e7e98eSRodrigo Siqueira } 53a4e7e98eSRodrigo Siqueira 5439cba5cfSMelissa Wen static u8 blend_channel(u8 src, u8 dst, u8 alpha) 5539cba5cfSMelissa Wen { 5639cba5cfSMelissa Wen u32 pre_blend; 5739cba5cfSMelissa Wen u8 new_color; 5839cba5cfSMelissa Wen 5939cba5cfSMelissa Wen pre_blend = (src * 255 + dst * (255 - alpha)); 6039cba5cfSMelissa Wen 6139cba5cfSMelissa Wen /* Faster div by 255 */ 6239cba5cfSMelissa Wen new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8); 6339cba5cfSMelissa Wen 6439cba5cfSMelissa Wen return new_color; 6539cba5cfSMelissa Wen } 6639cba5cfSMelissa Wen 6732a1648aSMelissa Wen /** 6832a1648aSMelissa Wen * alpha_blend - alpha blending equation 6932a1648aSMelissa Wen * @argb_src: src pixel on premultiplied alpha mode 7032a1648aSMelissa Wen * @argb_dst: dst pixel completely opaque 7132a1648aSMelissa Wen * 7232a1648aSMelissa Wen * blend pixels using premultiplied blend formula. The current DRM assumption 7332a1648aSMelissa Wen * is that pixel color values have been already pre-multiplied with the alpha 7432a1648aSMelissa Wen * channel values. See more drm_plane_create_blend_mode_property(). Also, this 7532a1648aSMelissa Wen * formula assumes a completely opaque background. 7632a1648aSMelissa Wen */ 7732a1648aSMelissa Wen static void alpha_blend(const u8 *argb_src, u8 *argb_dst) 7839cba5cfSMelissa Wen { 7939cba5cfSMelissa Wen u8 alpha; 8039cba5cfSMelissa Wen 8139cba5cfSMelissa Wen alpha = argb_src[3]; 8239cba5cfSMelissa Wen argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha); 8339cba5cfSMelissa Wen argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha); 8439cba5cfSMelissa Wen argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha); 8532a1648aSMelissa Wen } 8632a1648aSMelissa Wen 8732a1648aSMelissa Wen /** 8832a1648aSMelissa Wen * x_blend - blending equation that ignores the pixel alpha 8932a1648aSMelissa Wen * 9032a1648aSMelissa Wen * overwrites RGB color value from src pixel to dst pixel. 9132a1648aSMelissa Wen */ 9232a1648aSMelissa Wen static void x_blend(const u8 *xrgb_src, u8 *xrgb_dst) 9332a1648aSMelissa Wen { 9432a1648aSMelissa Wen memcpy(xrgb_dst, xrgb_src, sizeof(u8) * 3); 9539cba5cfSMelissa Wen } 9639cba5cfSMelissa Wen 97a4e7e98eSRodrigo Siqueira /** 980530bbd0SGabriela Bittencourt * blend - blend value at vaddr_src with value at vaddr_dst 99a4e7e98eSRodrigo Siqueira * @vaddr_dst: destination address 100a4e7e98eSRodrigo Siqueira * @vaddr_src: source address 101*1645e7b9SIgor Torrente * @dst_frame_info: destination framebuffer's metadata 102*1645e7b9SIgor Torrente * @src_frame_info: source framebuffer's metadata 10332a1648aSMelissa Wen * @pixel_blend: blending equation based on plane format 104a4e7e98eSRodrigo Siqueira * 10532a1648aSMelissa Wen * Blend the vaddr_src value with the vaddr_dst value using a pixel blend 10632a1648aSMelissa Wen * equation according to the supported plane formats DRM_FORMAT_(A/XRGB8888) 10732a1648aSMelissa Wen * and clearing alpha channel to an completely opaque background. This function 10832a1648aSMelissa Wen * uses buffer's metadata to locate the new composite values at vaddr_dst. 10932a1648aSMelissa Wen * 11032a1648aSMelissa Wen * TODO: completely clear the primary plane (a = 0xff) before starting to blend 11132a1648aSMelissa Wen * pixel color values 112a4e7e98eSRodrigo Siqueira */ 113a4e7e98eSRodrigo Siqueira static void blend(void *vaddr_dst, void *vaddr_src, 114*1645e7b9SIgor Torrente struct vkms_frame_info *dst_frame_info, 115*1645e7b9SIgor Torrente struct vkms_frame_info *src_frame_info, 11632a1648aSMelissa Wen void (*pixel_blend)(const u8 *, u8 *)) 117a4e7e98eSRodrigo Siqueira { 118a4e7e98eSRodrigo Siqueira int i, j, j_dst, i_dst; 119a4e7e98eSRodrigo Siqueira int offset_src, offset_dst; 12039cba5cfSMelissa Wen u8 *pixel_dst, *pixel_src; 121a4e7e98eSRodrigo Siqueira 122*1645e7b9SIgor Torrente int x_src = src_frame_info->src.x1 >> 16; 123*1645e7b9SIgor Torrente int y_src = src_frame_info->src.y1 >> 16; 124a4e7e98eSRodrigo Siqueira 125*1645e7b9SIgor Torrente int x_dst = src_frame_info->dst.x1; 126*1645e7b9SIgor Torrente int y_dst = src_frame_info->dst.y1; 127*1645e7b9SIgor Torrente int h_dst = drm_rect_height(&src_frame_info->dst); 128*1645e7b9SIgor Torrente int w_dst = drm_rect_width(&src_frame_info->dst); 129a4e7e98eSRodrigo Siqueira 130a4e7e98eSRodrigo Siqueira int y_limit = y_src + h_dst; 131a4e7e98eSRodrigo Siqueira int x_limit = x_src + w_dst; 132a4e7e98eSRodrigo Siqueira 133a4e7e98eSRodrigo Siqueira for (i = y_src, i_dst = y_dst; i < y_limit; ++i) { 134a4e7e98eSRodrigo Siqueira for (j = x_src, j_dst = x_dst; j < x_limit; ++j) { 135*1645e7b9SIgor Torrente offset_dst = dst_frame_info->offset 136*1645e7b9SIgor Torrente + (i_dst * dst_frame_info->pitch) 137*1645e7b9SIgor Torrente + (j_dst++ * dst_frame_info->cpp); 138*1645e7b9SIgor Torrente offset_src = src_frame_info->offset 139*1645e7b9SIgor Torrente + (i * src_frame_info->pitch) 140*1645e7b9SIgor Torrente + (j * src_frame_info->cpp); 141a4e7e98eSRodrigo Siqueira 14239cba5cfSMelissa Wen pixel_src = (u8 *)(vaddr_src + offset_src); 14339cba5cfSMelissa Wen pixel_dst = (u8 *)(vaddr_dst + offset_dst); 14432a1648aSMelissa Wen pixel_blend(pixel_src, pixel_dst); 14532a1648aSMelissa Wen /* clearing alpha channel (0xff)*/ 14632a1648aSMelissa Wen pixel_dst[3] = 0xff; 147a4e7e98eSRodrigo Siqueira } 148a4e7e98eSRodrigo Siqueira i_dst++; 149a4e7e98eSRodrigo Siqueira } 150a4e7e98eSRodrigo Siqueira } 151a4e7e98eSRodrigo Siqueira 152*1645e7b9SIgor Torrente static void compose_plane(struct vkms_frame_info *primary_plane_info, 153*1645e7b9SIgor Torrente struct vkms_frame_info *plane_frame_info, 154a4e7e98eSRodrigo Siqueira void *vaddr_out) 155a4e7e98eSRodrigo Siqueira { 156*1645e7b9SIgor Torrente struct drm_framebuffer *fb = &plane_frame_info->fb; 157bbeb7461SThomas Zimmermann void *vaddr; 15832a1648aSMelissa Wen void (*pixel_blend)(const u8 *p_src, u8 *p_dst); 159a4e7e98eSRodrigo Siqueira 160*1645e7b9SIgor Torrente if (WARN_ON(iosys_map_is_null(&plane_frame_info->map[0]))) 161a4e7e98eSRodrigo Siqueira return; 162a4e7e98eSRodrigo Siqueira 163*1645e7b9SIgor Torrente vaddr = plane_frame_info->map[0].vaddr; 164bbeb7461SThomas Zimmermann 16532a1648aSMelissa Wen if (fb->format->format == DRM_FORMAT_ARGB8888) 16632a1648aSMelissa Wen pixel_blend = &alpha_blend; 16732a1648aSMelissa Wen else 16832a1648aSMelissa Wen pixel_blend = &x_blend; 16932a1648aSMelissa Wen 170*1645e7b9SIgor Torrente blend(vaddr_out, vaddr, primary_plane_info, 171*1645e7b9SIgor Torrente plane_frame_info, pixel_blend); 172a4e7e98eSRodrigo Siqueira } 173a4e7e98eSRodrigo Siqueira 174cac80e71SMelissa Wen static int compose_active_planes(void **vaddr_out, 175*1645e7b9SIgor Torrente struct vkms_frame_info *primary_plane_info, 176310e506cSMelissa Wen struct vkms_crtc_state *crtc_state) 177a4e7e98eSRodrigo Siqueira { 178*1645e7b9SIgor Torrente struct drm_framebuffer *fb = &primary_plane_info->fb; 179a4e7e98eSRodrigo Siqueira struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0); 180bbeb7461SThomas Zimmermann const void *vaddr; 181310e506cSMelissa Wen int i; 182a4e7e98eSRodrigo Siqueira 18395302576SRodrigo Siqueira if (!*vaddr_out) { 18416490922SIgor Torrente *vaddr_out = kvzalloc(gem_obj->size, GFP_KERNEL); 18595302576SRodrigo Siqueira if (!*vaddr_out) { 18695302576SRodrigo Siqueira DRM_ERROR("Cannot allocate memory for output frame."); 18795302576SRodrigo Siqueira return -ENOMEM; 18895302576SRodrigo Siqueira } 189a4e7e98eSRodrigo Siqueira } 190a4e7e98eSRodrigo Siqueira 191*1645e7b9SIgor Torrente if (WARN_ON(iosys_map_is_null(&primary_plane_info->map[0]))) 19295302576SRodrigo Siqueira return -EINVAL; 193a4e7e98eSRodrigo Siqueira 194*1645e7b9SIgor Torrente vaddr = primary_plane_info->map[0].vaddr; 195bbeb7461SThomas Zimmermann 196bbeb7461SThomas Zimmermann memcpy(*vaddr_out, vaddr, gem_obj->size); 197a4e7e98eSRodrigo Siqueira 198310e506cSMelissa Wen /* If there are other planes besides primary, we consider the active 199310e506cSMelissa Wen * planes should be in z-order and compose them associatively: 200310e506cSMelissa Wen * ((primary <- overlay) <- cursor) 201310e506cSMelissa Wen */ 202310e506cSMelissa Wen for (i = 1; i < crtc_state->num_active_planes; i++) 203*1645e7b9SIgor Torrente compose_plane(primary_plane_info, 204*1645e7b9SIgor Torrente crtc_state->active_planes[i]->frame_info, 205310e506cSMelissa Wen *vaddr_out); 206a4e7e98eSRodrigo Siqueira 20795302576SRodrigo Siqueira return 0; 208a4e7e98eSRodrigo Siqueira } 209a4e7e98eSRodrigo Siqueira 210a4e7e98eSRodrigo Siqueira /** 211a4e7e98eSRodrigo Siqueira * vkms_composer_worker - ordered work_struct to compute CRC 212a4e7e98eSRodrigo Siqueira * 213a4e7e98eSRodrigo Siqueira * @work: work_struct 214a4e7e98eSRodrigo Siqueira * 215a4e7e98eSRodrigo Siqueira * Work handler for composing and computing CRCs. work_struct scheduled in 216a4e7e98eSRodrigo Siqueira * an ordered workqueue that's periodically scheduled to run by 217e3137249SAndré Almeida * vkms_vblank_simulate() and flushed at vkms_atomic_commit_tail(). 218a4e7e98eSRodrigo Siqueira */ 219a4e7e98eSRodrigo Siqueira void vkms_composer_worker(struct work_struct *work) 220a4e7e98eSRodrigo Siqueira { 221a4e7e98eSRodrigo Siqueira struct vkms_crtc_state *crtc_state = container_of(work, 222a4e7e98eSRodrigo Siqueira struct vkms_crtc_state, 223a4e7e98eSRodrigo Siqueira composer_work); 224a4e7e98eSRodrigo Siqueira struct drm_crtc *crtc = crtc_state->base.crtc; 225a4e7e98eSRodrigo Siqueira struct vkms_output *out = drm_crtc_to_vkms_output(crtc); 226*1645e7b9SIgor Torrente struct vkms_frame_info *primary_plane_info = NULL; 227310e506cSMelissa Wen struct vkms_plane_state *act_plane = NULL; 228dbd9d80cSRodrigo Siqueira bool crc_pending, wb_pending; 22995302576SRodrigo Siqueira void *vaddr_out = NULL; 230a4e7e98eSRodrigo Siqueira u32 crc32 = 0; 231a4e7e98eSRodrigo Siqueira u64 frame_start, frame_end; 23295302576SRodrigo Siqueira int ret; 233a4e7e98eSRodrigo Siqueira 234a4e7e98eSRodrigo Siqueira spin_lock_irq(&out->composer_lock); 235a4e7e98eSRodrigo Siqueira frame_start = crtc_state->frame_start; 236a4e7e98eSRodrigo Siqueira frame_end = crtc_state->frame_end; 237a4e7e98eSRodrigo Siqueira crc_pending = crtc_state->crc_pending; 238dbd9d80cSRodrigo Siqueira wb_pending = crtc_state->wb_pending; 239a4e7e98eSRodrigo Siqueira crtc_state->frame_start = 0; 240a4e7e98eSRodrigo Siqueira crtc_state->frame_end = 0; 241a4e7e98eSRodrigo Siqueira crtc_state->crc_pending = false; 242a4e7e98eSRodrigo Siqueira spin_unlock_irq(&out->composer_lock); 243a4e7e98eSRodrigo Siqueira 244a4e7e98eSRodrigo Siqueira /* 245a4e7e98eSRodrigo Siqueira * We raced with the vblank hrtimer and previous work already computed 246a4e7e98eSRodrigo Siqueira * the crc, nothing to do. 247a4e7e98eSRodrigo Siqueira */ 248a4e7e98eSRodrigo Siqueira if (!crc_pending) 249a4e7e98eSRodrigo Siqueira return; 250a4e7e98eSRodrigo Siqueira 251310e506cSMelissa Wen if (crtc_state->num_active_planes >= 1) { 252310e506cSMelissa Wen act_plane = crtc_state->active_planes[0]; 2537602d422SThomas Zimmermann if (act_plane->base.base.plane->type == DRM_PLANE_TYPE_PRIMARY) 254*1645e7b9SIgor Torrente primary_plane_info = act_plane->frame_info; 255310e506cSMelissa Wen } 256a4e7e98eSRodrigo Siqueira 257*1645e7b9SIgor Torrente if (!primary_plane_info) 25895302576SRodrigo Siqueira return; 25995302576SRodrigo Siqueira 260dbd9d80cSRodrigo Siqueira if (wb_pending) 2612ca380eaSThomas Zimmermann vaddr_out = crtc_state->active_writeback->data[0].vaddr; 262dbd9d80cSRodrigo Siqueira 263*1645e7b9SIgor Torrente ret = compose_active_planes(&vaddr_out, primary_plane_info, 264310e506cSMelissa Wen crtc_state); 26595302576SRodrigo Siqueira if (ret) { 266dbd9d80cSRodrigo Siqueira if (ret == -EINVAL && !wb_pending) 26716490922SIgor Torrente kvfree(vaddr_out); 26895302576SRodrigo Siqueira return; 26995302576SRodrigo Siqueira } 27095302576SRodrigo Siqueira 271*1645e7b9SIgor Torrente crc32 = compute_crc(vaddr_out, primary_plane_info); 272a4e7e98eSRodrigo Siqueira 273dbd9d80cSRodrigo Siqueira if (wb_pending) { 274dbd9d80cSRodrigo Siqueira drm_writeback_signal_completion(&out->wb_connector, 0); 275dbd9d80cSRodrigo Siqueira spin_lock_irq(&out->composer_lock); 276dbd9d80cSRodrigo Siqueira crtc_state->wb_pending = false; 277dbd9d80cSRodrigo Siqueira spin_unlock_irq(&out->composer_lock); 278dbd9d80cSRodrigo Siqueira } else { 27916490922SIgor Torrente kvfree(vaddr_out); 280dbd9d80cSRodrigo Siqueira } 281dbd9d80cSRodrigo Siqueira 282a4e7e98eSRodrigo Siqueira /* 283a4e7e98eSRodrigo Siqueira * The worker can fall behind the vblank hrtimer, make sure we catch up. 284a4e7e98eSRodrigo Siqueira */ 285a4e7e98eSRodrigo Siqueira while (frame_start <= frame_end) 286a4e7e98eSRodrigo Siqueira drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32); 287a4e7e98eSRodrigo Siqueira } 288a4e7e98eSRodrigo Siqueira 289a4e7e98eSRodrigo Siqueira static const char * const pipe_crc_sources[] = {"auto"}; 290a4e7e98eSRodrigo Siqueira 291a4e7e98eSRodrigo Siqueira const char *const *vkms_get_crc_sources(struct drm_crtc *crtc, 292a4e7e98eSRodrigo Siqueira size_t *count) 293a4e7e98eSRodrigo Siqueira { 294a4e7e98eSRodrigo Siqueira *count = ARRAY_SIZE(pipe_crc_sources); 295a4e7e98eSRodrigo Siqueira return pipe_crc_sources; 296a4e7e98eSRodrigo Siqueira } 297a4e7e98eSRodrigo Siqueira 298a4e7e98eSRodrigo Siqueira static int vkms_crc_parse_source(const char *src_name, bool *enabled) 299a4e7e98eSRodrigo Siqueira { 300a4e7e98eSRodrigo Siqueira int ret = 0; 301a4e7e98eSRodrigo Siqueira 302a4e7e98eSRodrigo Siqueira if (!src_name) { 303a4e7e98eSRodrigo Siqueira *enabled = false; 304a4e7e98eSRodrigo Siqueira } else if (strcmp(src_name, "auto") == 0) { 305a4e7e98eSRodrigo Siqueira *enabled = true; 306a4e7e98eSRodrigo Siqueira } else { 307a4e7e98eSRodrigo Siqueira *enabled = false; 308a4e7e98eSRodrigo Siqueira ret = -EINVAL; 309a4e7e98eSRodrigo Siqueira } 310a4e7e98eSRodrigo Siqueira 311a4e7e98eSRodrigo Siqueira return ret; 312a4e7e98eSRodrigo Siqueira } 313a4e7e98eSRodrigo Siqueira 314a4e7e98eSRodrigo Siqueira int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name, 315a4e7e98eSRodrigo Siqueira size_t *values_cnt) 316a4e7e98eSRodrigo Siqueira { 317a4e7e98eSRodrigo Siqueira bool enabled; 318a4e7e98eSRodrigo Siqueira 319a4e7e98eSRodrigo Siqueira if (vkms_crc_parse_source(src_name, &enabled) < 0) { 320a4e7e98eSRodrigo Siqueira DRM_DEBUG_DRIVER("unknown source %s\n", src_name); 321a4e7e98eSRodrigo Siqueira return -EINVAL; 322a4e7e98eSRodrigo Siqueira } 323a4e7e98eSRodrigo Siqueira 324a4e7e98eSRodrigo Siqueira *values_cnt = 1; 325a4e7e98eSRodrigo Siqueira 326a4e7e98eSRodrigo Siqueira return 0; 327a4e7e98eSRodrigo Siqueira } 328a4e7e98eSRodrigo Siqueira 329dbd9d80cSRodrigo Siqueira void vkms_set_composer(struct vkms_output *out, bool enabled) 3305bd858d7SMelissa Wen { 3315bd858d7SMelissa Wen bool old_enabled; 3325bd858d7SMelissa Wen 3335bd858d7SMelissa Wen if (enabled) 3345bd858d7SMelissa Wen drm_crtc_vblank_get(&out->crtc); 3355bd858d7SMelissa Wen 3365bd858d7SMelissa Wen spin_lock_irq(&out->lock); 3375bd858d7SMelissa Wen old_enabled = out->composer_enabled; 3385bd858d7SMelissa Wen out->composer_enabled = enabled; 3395bd858d7SMelissa Wen spin_unlock_irq(&out->lock); 3405bd858d7SMelissa Wen 3415bd858d7SMelissa Wen if (old_enabled) 3425bd858d7SMelissa Wen drm_crtc_vblank_put(&out->crtc); 3435bd858d7SMelissa Wen } 3445bd858d7SMelissa Wen 345a4e7e98eSRodrigo Siqueira int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name) 346a4e7e98eSRodrigo Siqueira { 347a4e7e98eSRodrigo Siqueira struct vkms_output *out = drm_crtc_to_vkms_output(crtc); 348a4e7e98eSRodrigo Siqueira bool enabled = false; 349a4e7e98eSRodrigo Siqueira int ret = 0; 350a4e7e98eSRodrigo Siqueira 351a4e7e98eSRodrigo Siqueira ret = vkms_crc_parse_source(src_name, &enabled); 352a4e7e98eSRodrigo Siqueira 3535bd858d7SMelissa Wen vkms_set_composer(out, enabled); 354a4e7e98eSRodrigo Siqueira 355a4e7e98eSRodrigo Siqueira return ret; 356a4e7e98eSRodrigo Siqueira } 357