vkms_crc.c 6.8 KB

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