1
2
3#include <linux/crc32.h>
4
5#include <drm/drm_atomic.h>
6#include <drm/drm_atomic_helper.h>
7#include <drm/drm_gem_framebuffer_helper.h>
8#include <drm/drm_vblank.h>
9
10#include "vkms_drv.h"
11
12
13
14
15
16
17
18
19
20
21static uint32_t compute_crc(void *vaddr_out, struct vkms_composer *composer)
22{
23 int i, j, src_offset;
24 int x_src = composer->src.x1 >> 16;
25 int y_src = composer->src.y1 >> 16;
26 int h_src = drm_rect_height(&composer->src) >> 16;
27 int w_src = drm_rect_width(&composer->src) >> 16;
28 u32 crc = 0;
29
30 for (i = y_src; i < y_src + h_src; ++i) {
31 for (j = x_src; j < x_src + w_src; ++j) {
32 src_offset = composer->offset
33 + (i * composer->pitch)
34 + (j * composer->cpp);
35
36 memset(vaddr_out + src_offset + 24, 0, 8);
37 crc = crc32_le(crc, vaddr_out + src_offset,
38 sizeof(u32));
39 }
40 }
41
42 return crc;
43}
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60static void blend(void *vaddr_dst, void *vaddr_src,
61 struct vkms_composer *dest_composer,
62 struct vkms_composer *src_composer)
63{
64 int i, j, j_dst, i_dst;
65 int offset_src, offset_dst;
66
67 int x_src = src_composer->src.x1 >> 16;
68 int y_src = src_composer->src.y1 >> 16;
69
70 int x_dst = src_composer->dst.x1;
71 int y_dst = src_composer->dst.y1;
72 int h_dst = drm_rect_height(&src_composer->dst);
73 int w_dst = drm_rect_width(&src_composer->dst);
74
75 int y_limit = y_src + h_dst;
76 int x_limit = x_src + w_dst;
77
78 for (i = y_src, i_dst = y_dst; i < y_limit; ++i) {
79 for (j = x_src, j_dst = x_dst; j < x_limit; ++j) {
80 offset_dst = dest_composer->offset
81 + (i_dst * dest_composer->pitch)
82 + (j_dst++ * dest_composer->cpp);
83 offset_src = src_composer->offset
84 + (i * src_composer->pitch)
85 + (j * src_composer->cpp);
86
87 memcpy(vaddr_dst + offset_dst,
88 vaddr_src + offset_src, sizeof(u32));
89 }
90 i_dst++;
91 }
92}
93
94static void compose_cursor(struct vkms_composer *cursor_composer,
95 struct vkms_composer *primary_composer,
96 void *vaddr_out)
97{
98 struct drm_gem_object *cursor_obj;
99 struct vkms_gem_object *cursor_vkms_obj;
100
101 cursor_obj = drm_gem_fb_get_obj(&cursor_composer->fb, 0);
102 cursor_vkms_obj = drm_gem_to_vkms_gem(cursor_obj);
103
104 if (WARN_ON(!cursor_vkms_obj->vaddr))
105 return;
106
107 blend(vaddr_out, cursor_vkms_obj->vaddr,
108 primary_composer, cursor_composer);
109}
110
111static uint32_t _vkms_get_crc(struct vkms_composer *primary_composer,
112 struct vkms_composer *cursor_composer)
113{
114 struct drm_framebuffer *fb = &primary_composer->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 if (WARN_ON(!vkms_obj->vaddr)) {
126 kfree(vaddr_out);
127 return crc;
128 }
129
130 memcpy(vaddr_out, vkms_obj->vaddr, vkms_obj->gem.size);
131
132 if (cursor_composer)
133 compose_cursor(cursor_composer, primary_composer, vaddr_out);
134
135 crc = compute_crc(vaddr_out, primary_composer);
136
137 kfree(vaddr_out);
138
139 return crc;
140}
141
142
143
144
145
146
147
148
149
150
151void vkms_composer_worker(struct work_struct *work)
152{
153 struct vkms_crtc_state *crtc_state = container_of(work,
154 struct vkms_crtc_state,
155 composer_work);
156 struct drm_crtc *crtc = crtc_state->base.crtc;
157 struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
158 struct vkms_composer *primary_composer = NULL;
159 struct vkms_composer *cursor_composer = NULL;
160 u32 crc32 = 0;
161 u64 frame_start, frame_end;
162 bool crc_pending;
163
164 spin_lock_irq(&out->composer_lock);
165 frame_start = crtc_state->frame_start;
166 frame_end = crtc_state->frame_end;
167 crc_pending = crtc_state->crc_pending;
168 crtc_state->frame_start = 0;
169 crtc_state->frame_end = 0;
170 crtc_state->crc_pending = false;
171 spin_unlock_irq(&out->composer_lock);
172
173
174
175
176
177 if (!crc_pending)
178 return;
179
180 if (crtc_state->num_active_planes >= 1)
181 primary_composer = crtc_state->active_planes[0]->composer;
182
183 if (crtc_state->num_active_planes == 2)
184 cursor_composer = crtc_state->active_planes[1]->composer;
185
186 if (primary_composer)
187 crc32 = _vkms_get_crc(primary_composer, cursor_composer);
188
189
190
191
192 while (frame_start <= frame_end)
193 drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32);
194}
195
196static const char * const pipe_crc_sources[] = {"auto"};
197
198const char *const *vkms_get_crc_sources(struct drm_crtc *crtc,
199 size_t *count)
200{
201 *count = ARRAY_SIZE(pipe_crc_sources);
202 return pipe_crc_sources;
203}
204
205static int vkms_crc_parse_source(const char *src_name, bool *enabled)
206{
207 int ret = 0;
208
209 if (!src_name) {
210 *enabled = false;
211 } else if (strcmp(src_name, "auto") == 0) {
212 *enabled = true;
213 } else {
214 *enabled = false;
215 ret = -EINVAL;
216 }
217
218 return ret;
219}
220
221int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
222 size_t *values_cnt)
223{
224 bool enabled;
225
226 if (vkms_crc_parse_source(src_name, &enabled) < 0) {
227 DRM_DEBUG_DRIVER("unknown source %s\n", src_name);
228 return -EINVAL;
229 }
230
231 *values_cnt = 1;
232
233 return 0;
234}
235
236int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name)
237{
238 struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
239 bool enabled = false;
240 int ret = 0;
241
242 ret = vkms_crc_parse_source(src_name, &enabled);
243
244 spin_lock_irq(&out->lock);
245 out->composer_enabled = enabled;
246 spin_unlock_irq(&out->lock);
247
248 return ret;
249}
250