1 | /* |
2 | * Copyright © 2008 Intel Corporation |
3 | * Copyright © 2016 Collabora Ltd |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a |
6 | * copy of this software and associated documentation files (the "Software"), |
7 | * to deal in the Software without restriction, including without limitation |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
9 | * and/or sell copies of the Software, and to permit persons to whom the |
10 | * Software is furnished to do so, subject to the following conditions: |
11 | * |
12 | * The above copyright notice and this permission notice (including the next |
13 | * paragraph) shall be included in all copies or substantial portions of the |
14 | * Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
22 | * IN THE SOFTWARE. |
23 | * |
24 | * Based on code from the i915 driver. |
25 | * Original author: Damien Lespiau <damien.lespiau@intel.com> |
26 | * |
27 | */ |
28 | |
29 | #include <linux/circ_buf.h> |
30 | #include <linux/ctype.h> |
31 | #include <linux/debugfs.h> |
32 | #include <linux/poll.h> |
33 | #include <linux/uaccess.h> |
34 | |
35 | #include <drm/drm_crtc.h> |
36 | #include <drm/drm_debugfs_crc.h> |
37 | #include <drm/drm_drv.h> |
38 | #include <drm/drm_print.h> |
39 | |
40 | #include "drm_internal.h" |
41 | |
42 | /** |
43 | * DOC: CRC ABI |
44 | * |
45 | * DRM device drivers can provide to userspace CRC information of each frame as |
46 | * it reached a given hardware component (a CRC sampling "source"). |
47 | * |
48 | * Userspace can control generation of CRCs in a given CRTC by writing to the |
49 | * file dri/0/crtc-N/crc/control in debugfs, with N being the :ref:`index of |
50 | * the CRTC<crtc_index>`. Accepted values are source names (which are |
51 | * driver-specific) and the "auto" keyword, which will let the driver select a |
52 | * default source of frame CRCs for this CRTC. |
53 | * |
54 | * Once frame CRC generation is enabled, userspace can capture them by reading |
55 | * the dri/0/crtc-N/crc/data file. Each line in that file contains the frame |
56 | * number in the first field and then a number of unsigned integer fields |
57 | * containing the CRC data. Fields are separated by a single space and the number |
58 | * of CRC fields is source-specific. |
59 | * |
60 | * Note that though in some cases the CRC is computed in a specified way and on |
61 | * the frame contents as supplied by userspace (eDP 1.3), in general the CRC |
62 | * computation is performed in an unspecified way and on frame contents that have |
63 | * been already processed in also an unspecified way and thus userspace cannot |
64 | * rely on being able to generate matching CRC values for the frame contents that |
65 | * it submits. In this general case, the maximum userspace can do is to compare |
66 | * the reported CRCs of frames that should have the same contents. |
67 | * |
68 | * On the driver side the implementation effort is minimal, drivers only need to |
69 | * implement &drm_crtc_funcs.set_crc_source and &drm_crtc_funcs.verify_crc_source. |
70 | * The debugfs files are automatically set up if those vfuncs are set. CRC samples |
71 | * need to be captured in the driver by calling drm_crtc_add_crc_entry(). |
72 | * Depending on the driver and HW requirements, &drm_crtc_funcs.set_crc_source |
73 | * may result in a commit (even a full modeset). |
74 | * |
75 | * CRC results must be reliable across non-full-modeset atomic commits, so if a |
76 | * commit via DRM_IOCTL_MODE_ATOMIC would disable or otherwise interfere with |
77 | * CRC generation, then the driver must mark that commit as a full modeset |
78 | * (drm_atomic_crtc_needs_modeset() should return true). As a result, to ensure |
79 | * consistent results, generic userspace must re-setup CRC generation after a |
80 | * legacy SETCRTC or an atomic commit with DRM_MODE_ATOMIC_ALLOW_MODESET. |
81 | */ |
82 | |
83 | static int crc_control_show(struct seq_file *m, void *data) |
84 | { |
85 | struct drm_crtc *crtc = m->private; |
86 | |
87 | if (crtc->funcs->get_crc_sources) { |
88 | size_t count; |
89 | const char *const *sources = crtc->funcs->get_crc_sources(crtc, |
90 | &count); |
91 | size_t values_cnt; |
92 | int i; |
93 | |
94 | if (count == 0 || !sources) |
95 | goto out; |
96 | |
97 | for (i = 0; i < count; i++) |
98 | if (!crtc->funcs->verify_crc_source(crtc, sources[i], |
99 | &values_cnt)) { |
100 | if (strcmp(sources[i], crtc->crc.source)) |
101 | seq_printf(m, fmt: "%s\n" , sources[i]); |
102 | else |
103 | seq_printf(m, fmt: "%s*\n" , sources[i]); |
104 | } |
105 | } |
106 | return 0; |
107 | |
108 | out: |
109 | seq_printf(m, fmt: "%s*\n" , crtc->crc.source); |
110 | return 0; |
111 | } |
112 | |
113 | static int crc_control_open(struct inode *inode, struct file *file) |
114 | { |
115 | struct drm_crtc *crtc = inode->i_private; |
116 | |
117 | return single_open(file, crc_control_show, crtc); |
118 | } |
119 | |
120 | static ssize_t crc_control_write(struct file *file, const char __user *ubuf, |
121 | size_t len, loff_t *offp) |
122 | { |
123 | struct seq_file *m = file->private_data; |
124 | struct drm_crtc *crtc = m->private; |
125 | struct drm_crtc_crc *crc = &crtc->crc; |
126 | char *source; |
127 | size_t values_cnt; |
128 | int ret; |
129 | |
130 | if (len == 0) |
131 | return 0; |
132 | |
133 | if (len > PAGE_SIZE - 1) { |
134 | DRM_DEBUG_KMS("Expected < %lu bytes into crtc crc control\n" , |
135 | PAGE_SIZE); |
136 | return -E2BIG; |
137 | } |
138 | |
139 | source = memdup_user_nul(ubuf, len); |
140 | if (IS_ERR(ptr: source)) |
141 | return PTR_ERR(ptr: source); |
142 | |
143 | if (source[len - 1] == '\n') |
144 | source[len - 1] = '\0'; |
145 | |
146 | ret = crtc->funcs->verify_crc_source(crtc, source, &values_cnt); |
147 | if (ret) { |
148 | kfree(objp: source); |
149 | return ret; |
150 | } |
151 | |
152 | spin_lock_irq(lock: &crc->lock); |
153 | |
154 | if (crc->opened) { |
155 | spin_unlock_irq(lock: &crc->lock); |
156 | kfree(objp: source); |
157 | return -EBUSY; |
158 | } |
159 | |
160 | kfree(objp: crc->source); |
161 | crc->source = source; |
162 | |
163 | spin_unlock_irq(lock: &crc->lock); |
164 | |
165 | *offp += len; |
166 | return len; |
167 | } |
168 | |
169 | static const struct file_operations drm_crtc_crc_control_fops = { |
170 | .owner = THIS_MODULE, |
171 | .open = crc_control_open, |
172 | .read = seq_read, |
173 | .llseek = seq_lseek, |
174 | .release = single_release, |
175 | .write = crc_control_write |
176 | }; |
177 | |
178 | static int crtc_crc_data_count(struct drm_crtc_crc *crc) |
179 | { |
180 | assert_spin_locked(&crc->lock); |
181 | return CIRC_CNT(crc->head, crc->tail, DRM_CRC_ENTRIES_NR); |
182 | } |
183 | |
184 | static void crtc_crc_cleanup(struct drm_crtc_crc *crc) |
185 | { |
186 | kfree(objp: crc->entries); |
187 | crc->overflow = false; |
188 | crc->entries = NULL; |
189 | crc->head = 0; |
190 | crc->tail = 0; |
191 | crc->values_cnt = 0; |
192 | crc->opened = false; |
193 | } |
194 | |
195 | static int crtc_crc_open(struct inode *inode, struct file *filep) |
196 | { |
197 | struct drm_crtc *crtc = inode->i_private; |
198 | struct drm_crtc_crc *crc = &crtc->crc; |
199 | struct drm_crtc_crc_entry *entries = NULL; |
200 | size_t values_cnt; |
201 | int ret = 0; |
202 | |
203 | if (drm_drv_uses_atomic_modeset(dev: crtc->dev)) { |
204 | ret = drm_modeset_lock_single_interruptible(lock: &crtc->mutex); |
205 | if (ret) |
206 | return ret; |
207 | |
208 | if (!crtc->state->active) |
209 | ret = -EIO; |
210 | drm_modeset_unlock(lock: &crtc->mutex); |
211 | |
212 | if (ret) |
213 | return ret; |
214 | } |
215 | |
216 | ret = crtc->funcs->verify_crc_source(crtc, crc->source, &values_cnt); |
217 | if (ret) |
218 | return ret; |
219 | |
220 | if (WARN_ON(values_cnt > DRM_MAX_CRC_NR)) |
221 | return -EINVAL; |
222 | |
223 | if (WARN_ON(values_cnt == 0)) |
224 | return -EINVAL; |
225 | |
226 | entries = kcalloc(DRM_CRC_ENTRIES_NR, size: sizeof(*entries), GFP_KERNEL); |
227 | if (!entries) |
228 | return -ENOMEM; |
229 | |
230 | spin_lock_irq(lock: &crc->lock); |
231 | if (!crc->opened) { |
232 | crc->opened = true; |
233 | crc->entries = entries; |
234 | crc->values_cnt = values_cnt; |
235 | } else { |
236 | ret = -EBUSY; |
237 | } |
238 | spin_unlock_irq(lock: &crc->lock); |
239 | |
240 | if (ret) { |
241 | kfree(objp: entries); |
242 | return ret; |
243 | } |
244 | |
245 | ret = crtc->funcs->set_crc_source(crtc, crc->source); |
246 | if (ret) |
247 | goto err; |
248 | |
249 | return 0; |
250 | |
251 | err: |
252 | spin_lock_irq(lock: &crc->lock); |
253 | crtc_crc_cleanup(crc); |
254 | spin_unlock_irq(lock: &crc->lock); |
255 | return ret; |
256 | } |
257 | |
258 | static int crtc_crc_release(struct inode *inode, struct file *filep) |
259 | { |
260 | struct drm_crtc *crtc = filep->f_inode->i_private; |
261 | struct drm_crtc_crc *crc = &crtc->crc; |
262 | |
263 | /* terminate the infinite while loop if 'drm_dp_aux_crc_work' running */ |
264 | spin_lock_irq(lock: &crc->lock); |
265 | crc->opened = false; |
266 | spin_unlock_irq(lock: &crc->lock); |
267 | |
268 | crtc->funcs->set_crc_source(crtc, NULL); |
269 | |
270 | spin_lock_irq(lock: &crc->lock); |
271 | crtc_crc_cleanup(crc); |
272 | spin_unlock_irq(lock: &crc->lock); |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | /* |
278 | * 1 frame field of 10 chars plus a number of CRC fields of 10 chars each, space |
279 | * separated, with a newline at the end and null-terminated. |
280 | */ |
281 | #define LINE_LEN(values_cnt) (10 + 11 * values_cnt + 1 + 1) |
282 | #define MAX_LINE_LEN (LINE_LEN(DRM_MAX_CRC_NR)) |
283 | |
284 | static ssize_t crtc_crc_read(struct file *filep, char __user *user_buf, |
285 | size_t count, loff_t *pos) |
286 | { |
287 | struct drm_crtc *crtc = filep->f_inode->i_private; |
288 | struct drm_crtc_crc *crc = &crtc->crc; |
289 | struct drm_crtc_crc_entry *entry; |
290 | char buf[MAX_LINE_LEN]; |
291 | int ret, i; |
292 | |
293 | spin_lock_irq(lock: &crc->lock); |
294 | |
295 | if (!crc->source) { |
296 | spin_unlock_irq(lock: &crc->lock); |
297 | return 0; |
298 | } |
299 | |
300 | /* Nothing to read? */ |
301 | while (crtc_crc_data_count(crc) == 0) { |
302 | if (filep->f_flags & O_NONBLOCK) { |
303 | spin_unlock_irq(lock: &crc->lock); |
304 | return -EAGAIN; |
305 | } |
306 | |
307 | ret = wait_event_interruptible_lock_irq(crc->wq, |
308 | crtc_crc_data_count(crc), |
309 | crc->lock); |
310 | if (ret) { |
311 | spin_unlock_irq(lock: &crc->lock); |
312 | return ret; |
313 | } |
314 | } |
315 | |
316 | /* We know we have an entry to be read */ |
317 | entry = &crc->entries[crc->tail]; |
318 | |
319 | if (count < LINE_LEN(crc->values_cnt)) { |
320 | spin_unlock_irq(lock: &crc->lock); |
321 | return -EINVAL; |
322 | } |
323 | |
324 | BUILD_BUG_ON_NOT_POWER_OF_2(DRM_CRC_ENTRIES_NR); |
325 | crc->tail = (crc->tail + 1) & (DRM_CRC_ENTRIES_NR - 1); |
326 | |
327 | spin_unlock_irq(lock: &crc->lock); |
328 | |
329 | if (entry->has_frame_counter) |
330 | sprintf(buf, fmt: "0x%08x" , entry->frame); |
331 | else |
332 | sprintf(buf, fmt: "XXXXXXXXXX" ); |
333 | |
334 | for (i = 0; i < crc->values_cnt; i++) |
335 | sprintf(buf: buf + 10 + i * 11, fmt: " 0x%08x" , entry->crcs[i]); |
336 | sprintf(buf: buf + 10 + crc->values_cnt * 11, fmt: "\n" ); |
337 | |
338 | if (copy_to_user(to: user_buf, from: buf, LINE_LEN(crc->values_cnt))) |
339 | return -EFAULT; |
340 | |
341 | return LINE_LEN(crc->values_cnt); |
342 | } |
343 | |
344 | static __poll_t crtc_crc_poll(struct file *file, poll_table *wait) |
345 | { |
346 | struct drm_crtc *crtc = file->f_inode->i_private; |
347 | struct drm_crtc_crc *crc = &crtc->crc; |
348 | __poll_t ret = 0; |
349 | |
350 | poll_wait(filp: file, wait_address: &crc->wq, p: wait); |
351 | |
352 | spin_lock_irq(lock: &crc->lock); |
353 | if (crc->source && crtc_crc_data_count(crc)) |
354 | ret |= EPOLLIN | EPOLLRDNORM; |
355 | spin_unlock_irq(lock: &crc->lock); |
356 | |
357 | return ret; |
358 | } |
359 | |
360 | static const struct file_operations drm_crtc_crc_data_fops = { |
361 | .owner = THIS_MODULE, |
362 | .open = crtc_crc_open, |
363 | .read = crtc_crc_read, |
364 | .poll = crtc_crc_poll, |
365 | .release = crtc_crc_release, |
366 | }; |
367 | |
368 | void drm_debugfs_crtc_crc_add(struct drm_crtc *crtc) |
369 | { |
370 | struct dentry *crc_ent; |
371 | |
372 | if (!crtc->funcs->set_crc_source || !crtc->funcs->verify_crc_source) |
373 | return; |
374 | |
375 | crc_ent = debugfs_create_dir(name: "crc" , parent: crtc->debugfs_entry); |
376 | |
377 | debugfs_create_file(name: "control" , S_IRUGO | S_IWUSR, parent: crc_ent, data: crtc, |
378 | fops: &drm_crtc_crc_control_fops); |
379 | debugfs_create_file(name: "data" , S_IRUGO, parent: crc_ent, data: crtc, |
380 | fops: &drm_crtc_crc_data_fops); |
381 | } |
382 | |
383 | /** |
384 | * drm_crtc_add_crc_entry - Add entry with CRC information for a frame |
385 | * @crtc: CRTC to which the frame belongs |
386 | * @has_frame: whether this entry has a frame number to go with |
387 | * @frame: number of the frame these CRCs are about |
388 | * @crcs: array of CRC values, with length matching #drm_crtc_crc.values_cnt |
389 | * |
390 | * For each frame, the driver polls the source of CRCs for new data and calls |
391 | * this function to add them to the buffer from where userspace reads. |
392 | */ |
393 | int drm_crtc_add_crc_entry(struct drm_crtc *crtc, bool has_frame, |
394 | uint32_t frame, uint32_t *crcs) |
395 | { |
396 | struct drm_crtc_crc *crc = &crtc->crc; |
397 | struct drm_crtc_crc_entry *entry; |
398 | int head, tail; |
399 | unsigned long flags; |
400 | |
401 | spin_lock_irqsave(&crc->lock, flags); |
402 | |
403 | /* Caller may not have noticed yet that userspace has stopped reading */ |
404 | if (!crc->entries) { |
405 | spin_unlock_irqrestore(lock: &crc->lock, flags); |
406 | return -EINVAL; |
407 | } |
408 | |
409 | head = crc->head; |
410 | tail = crc->tail; |
411 | |
412 | if (CIRC_SPACE(head, tail, DRM_CRC_ENTRIES_NR) < 1) { |
413 | bool was_overflow = crc->overflow; |
414 | |
415 | crc->overflow = true; |
416 | spin_unlock_irqrestore(lock: &crc->lock, flags); |
417 | |
418 | if (!was_overflow) |
419 | DRM_ERROR("Overflow of CRC buffer, userspace reads too slow.\n" ); |
420 | |
421 | return -ENOBUFS; |
422 | } |
423 | |
424 | entry = &crc->entries[head]; |
425 | entry->frame = frame; |
426 | entry->has_frame_counter = has_frame; |
427 | memcpy(&entry->crcs, crcs, sizeof(*crcs) * crc->values_cnt); |
428 | |
429 | head = (head + 1) & (DRM_CRC_ENTRIES_NR - 1); |
430 | crc->head = head; |
431 | |
432 | spin_unlock_irqrestore(lock: &crc->lock, flags); |
433 | |
434 | wake_up_interruptible(&crc->wq); |
435 | |
436 | return 0; |
437 | } |
438 | EXPORT_SYMBOL_GPL(drm_crtc_add_crc_entry); |
439 | |