]>
Commit | Line | Data |
---|---|---|
9edbf1fa TV |
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> | |
0500c04e SR |
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 | ||
e3f56b29 | 40 | #include "drm_internal.h" |
9edbf1fa TV |
41 | |
42 | /** | |
43 | * DOC: CRC ABI | |
44 | * | |
45 | * DRM device drivers can provide to userspace CRC information of each frame as | |
760f71e7 | 46 | * it reached a given hardware component (a CRC sampling "source"). |
9edbf1fa TV |
47 | * |
48 | * Userspace can control generation of CRCs in a given CRTC by writing to the | |
26594678 LR |
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. | |
9edbf1fa TV |
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. | |
760f71e7 DV |
67 | * |
68 | * On the driver side the implementation effort is minimal, drivers only need to | |
84a68104 LD |
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(). | |
178e5f3a BS |
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. | |
9edbf1fa TV |
81 | */ |
82 | ||
83 | static int crc_control_show(struct seq_file *m, void *data) | |
84 | { | |
85 | struct drm_crtc *crtc = m->private; | |
86 | ||
4396551e MK |
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; | |
9edbf1fa | 93 | |
4396551e MK |
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, "%s\n", sources[i]); | |
102 | else | |
103 | seq_printf(m, "%s*\n", sources[i]); | |
104 | } | |
105 | } | |
106 | return 0; | |
107 | ||
108 | out: | |
109 | seq_printf(m, "%s*\n", crtc->crc.source); | |
9edbf1fa TV |
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; | |
d5cc15a0 MK |
127 | size_t values_cnt; |
128 | int ret; | |
9edbf1fa TV |
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(source)) | |
141 | return PTR_ERR(source); | |
142 | ||
72a848f5 DZ |
143 | if (source[len - 1] == '\n') |
144 | source[len - 1] = '\0'; | |
9edbf1fa | 145 | |
c0811a7d | 146 | ret = crtc->funcs->verify_crc_source(crtc, source, &values_cnt); |
f7ec68b3 DL |
147 | if (ret) { |
148 | kfree(source); | |
c0811a7d | 149 | return ret; |
f7ec68b3 | 150 | } |
d5cc15a0 | 151 | |
9edbf1fa TV |
152 | spin_lock_irq(&crc->lock); |
153 | ||
154 | if (crc->opened) { | |
155 | spin_unlock_irq(&crc->lock); | |
156 | kfree(source); | |
157 | return -EBUSY; | |
158 | } | |
159 | ||
160 | kfree(crc->source); | |
161 | crc->source = source; | |
162 | ||
163 | spin_unlock_irq(&crc->lock); | |
164 | ||
165 | *offp += len; | |
166 | return len; | |
167 | } | |
168 | ||
e3f56b29 | 169 | static const struct file_operations drm_crtc_crc_control_fops = { |
9edbf1fa TV |
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 | ||
e8fa5671 TV |
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 | ||
eb42ea6d ML |
184 | static void crtc_crc_cleanup(struct drm_crtc_crc *crc) |
185 | { | |
186 | kfree(crc->entries); | |
a0120245 | 187 | crc->overflow = false; |
eb42ea6d ML |
188 | crc->entries = NULL; |
189 | crc->head = 0; | |
190 | crc->tail = 0; | |
191 | crc->values_cnt = 0; | |
192 | crc->opened = false; | |
193 | } | |
194 | ||
9edbf1fa TV |
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; | |
eb42ea6d | 201 | int ret = 0; |
9edbf1fa | 202 | |
8038e09b | 203 | if (drm_drv_uses_atomic_modeset(crtc->dev)) { |
6f8bcc74 | 204 | ret = drm_modeset_lock_single_interruptible(&crtc->mutex); |
8038e09b ML |
205 | if (ret) |
206 | return ret; | |
207 | ||
208 | if (!crtc->state->active) | |
209 | ret = -EIO; | |
210 | drm_modeset_unlock(&crtc->mutex); | |
211 | ||
212 | if (ret) | |
213 | return ret; | |
214 | } | |
215 | ||
c0811a7d MK |
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, sizeof(*entries), GFP_KERNEL); | |
227 | if (!entries) | |
228 | return -ENOMEM; | |
229 | ||
eb42ea6d | 230 | spin_lock_irq(&crc->lock); |
c0811a7d | 231 | if (!crc->opened) { |
eb42ea6d | 232 | crc->opened = true; |
c0811a7d MK |
233 | crc->entries = entries; |
234 | crc->values_cnt = values_cnt; | |
235 | } else { | |
eb42ea6d | 236 | ret = -EBUSY; |
c0811a7d | 237 | } |
eb42ea6d | 238 | spin_unlock_irq(&crc->lock); |
9edbf1fa | 239 | |
c0811a7d MK |
240 | if (ret) { |
241 | kfree(entries); | |
9edbf1fa | 242 | return ret; |
c0811a7d | 243 | } |
9edbf1fa | 244 | |
c0811a7d | 245 | ret = crtc->funcs->set_crc_source(crtc, crc->source); |
eb42ea6d ML |
246 | if (ret) |
247 | goto err; | |
248 | ||
9edbf1fa TV |
249 | return 0; |
250 | ||
eb42ea6d ML |
251 | err: |
252 | spin_lock_irq(&crc->lock); | |
253 | crtc_crc_cleanup(crc); | |
254 | spin_unlock_irq(&crc->lock); | |
9edbf1fa TV |
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; | |
9edbf1fa | 262 | |
292f3e49 DZ |
263 | /* terminate the infinite while loop if 'drm_dp_aux_crc_work' running */ |
264 | spin_lock_irq(&crc->lock); | |
265 | crc->opened = false; | |
266 | spin_unlock_irq(&crc->lock); | |
267 | ||
c0811a7d | 268 | crtc->funcs->set_crc_source(crtc, NULL); |
eb42ea6d | 269 | |
9edbf1fa | 270 | spin_lock_irq(&crc->lock); |
eb42ea6d | 271 | crtc_crc_cleanup(crc); |
9edbf1fa TV |
272 | spin_unlock_irq(&crc->lock); |
273 | ||
9edbf1fa TV |
274 | return 0; |
275 | } | |
276 | ||
9edbf1fa TV |
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(&crc->lock); | |
294 | ||
295 | if (!crc->source) { | |
296 | spin_unlock_irq(&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(&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(&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(&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(&crc->lock); | |
328 | ||
329 | if (entry->has_frame_counter) | |
330 | sprintf(buf, "0x%08x", entry->frame); | |
331 | else | |
332 | sprintf(buf, "XXXXXXXXXX"); | |
333 | ||
334 | for (i = 0; i < crc->values_cnt; i++) | |
335 | sprintf(buf + 10 + i * 11, " 0x%08x", entry->crcs[i]); | |
336 | sprintf(buf + 10 + crc->values_cnt * 11, "\n"); | |
337 | ||
338 | if (copy_to_user(user_buf, buf, LINE_LEN(crc->values_cnt))) | |
339 | return -EFAULT; | |
340 | ||
341 | return LINE_LEN(crc->values_cnt); | |
342 | } | |
343 | ||
1ab2a99e | 344 | static __poll_t crtc_crc_poll(struct file *file, poll_table *wait) |
4beb3b40 ML |
345 | { |
346 | struct drm_crtc *crtc = file->f_inode->i_private; | |
347 | struct drm_crtc_crc *crc = &crtc->crc; | |
1ab2a99e | 348 | __poll_t ret = 0; |
4beb3b40 ML |
349 | |
350 | poll_wait(file, &crc->wq, wait); | |
351 | ||
352 | spin_lock_irq(&crc->lock); | |
353 | if (crc->source && crtc_crc_data_count(crc)) | |
1ab2a99e | 354 | ret |= EPOLLIN | EPOLLRDNORM; |
4beb3b40 ML |
355 | spin_unlock_irq(&crc->lock); |
356 | ||
357 | return ret; | |
358 | } | |
359 | ||
e3f56b29 | 360 | static const struct file_operations drm_crtc_crc_data_fops = { |
9edbf1fa TV |
361 | .owner = THIS_MODULE, |
362 | .open = crtc_crc_open, | |
363 | .read = crtc_crc_read, | |
4beb3b40 | 364 | .poll = crtc_crc_poll, |
9edbf1fa TV |
365 | .release = crtc_crc_release, |
366 | }; | |
367 | ||
b792e640 | 368 | void drm_debugfs_crtc_crc_add(struct drm_crtc *crtc) |
9edbf1fa | 369 | { |
b792e640 | 370 | struct dentry *crc_ent; |
9edbf1fa | 371 | |
c0811a7d | 372 | if (!crtc->funcs->set_crc_source || !crtc->funcs->verify_crc_source) |
b792e640 | 373 | return; |
9edbf1fa TV |
374 | |
375 | crc_ent = debugfs_create_dir("crc", crtc->debugfs_entry); | |
9edbf1fa | 376 | |
3cb6d8e5 | 377 | debugfs_create_file("control", S_IRUGO | S_IWUSR, crc_ent, crtc, |
b792e640 GKH |
378 | &drm_crtc_crc_control_fops); |
379 | debugfs_create_file("data", S_IRUGO, crc_ent, crtc, | |
380 | &drm_crtc_crc_data_fops); | |
9edbf1fa TV |
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; | |
1882018a | 399 | unsigned long flags; |
9edbf1fa | 400 | |
1882018a | 401 | spin_lock_irqsave(&crc->lock, flags); |
9edbf1fa TV |
402 | |
403 | /* Caller may not have noticed yet that userspace has stopped reading */ | |
eb42ea6d | 404 | if (!crc->entries) { |
d99004d7 | 405 | spin_unlock_irqrestore(&crc->lock, flags); |
9edbf1fa | 406 | return -EINVAL; |
1aa81be3 | 407 | } |
9edbf1fa TV |
408 | |
409 | head = crc->head; | |
410 | tail = crc->tail; | |
411 | ||
412 | if (CIRC_SPACE(head, tail, DRM_CRC_ENTRIES_NR) < 1) { | |
a0120245 ML |
413 | bool was_overflow = crc->overflow; |
414 | ||
415 | crc->overflow = true; | |
d99004d7 | 416 | spin_unlock_irqrestore(&crc->lock, flags); |
a0120245 ML |
417 | |
418 | if (!was_overflow) | |
419 | DRM_ERROR("Overflow of CRC buffer, userspace reads too slow.\n"); | |
420 | ||
9edbf1fa TV |
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 | ||
1882018a | 432 | spin_unlock_irqrestore(&crc->lock, flags); |
1aa81be3 | 433 | |
9f547425 BG |
434 | wake_up_interruptible(&crc->wq); |
435 | ||
9edbf1fa TV |
436 | return 0; |
437 | } | |
438 | EXPORT_SYMBOL_GPL(drm_crtc_add_crc_entry); |