diff options
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 566 |
1 files changed, 541 insertions, 25 deletions
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 16d5155edad1..0054e957203f 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -40,6 +40,22 @@ | |||
40 | #include <linux/slab.h> | 40 | #include <linux/slab.h> |
41 | 41 | ||
42 | #include <linux/vgaarb.h> | 42 | #include <linux/vgaarb.h> |
43 | |||
44 | /* Access macro for slots in vblank timestamp ringbuffer. */ | ||
45 | #define vblanktimestamp(dev, crtc, count) ( \ | ||
46 | (dev)->_vblank_time[(crtc) * DRM_VBLANKTIME_RBSIZE + \ | ||
47 | ((count) % DRM_VBLANKTIME_RBSIZE)]) | ||
48 | |||
49 | /* Retry timestamp calculation up to 3 times to satisfy | ||
50 | * drm_timestamp_precision before giving up. | ||
51 | */ | ||
52 | #define DRM_TIMESTAMP_MAXRETRIES 3 | ||
53 | |||
54 | /* Threshold in nanoseconds for detection of redundant | ||
55 | * vblank irq in drm_handle_vblank(). 1 msec should be ok. | ||
56 | */ | ||
57 | #define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000 | ||
58 | |||
43 | /** | 59 | /** |
44 | * Get interrupt from bus id. | 60 | * Get interrupt from bus id. |
45 | * | 61 | * |
@@ -77,6 +93,87 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, | |||
77 | return 0; | 93 | return 0; |
78 | } | 94 | } |
79 | 95 | ||
96 | /* | ||
97 | * Clear vblank timestamp buffer for a crtc. | ||
98 | */ | ||
99 | static void clear_vblank_timestamps(struct drm_device *dev, int crtc) | ||
100 | { | ||
101 | memset(&dev->_vblank_time[crtc * DRM_VBLANKTIME_RBSIZE], 0, | ||
102 | DRM_VBLANKTIME_RBSIZE * sizeof(struct timeval)); | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * Disable vblank irq's on crtc, make sure that last vblank count | ||
107 | * of hardware and corresponding consistent software vblank counter | ||
108 | * are preserved, even if there are any spurious vblank irq's after | ||
109 | * disable. | ||
110 | */ | ||
111 | static void vblank_disable_and_save(struct drm_device *dev, int crtc) | ||
112 | { | ||
113 | unsigned long irqflags; | ||
114 | u32 vblcount; | ||
115 | s64 diff_ns; | ||
116 | int vblrc; | ||
117 | struct timeval tvblank; | ||
118 | |||
119 | /* Prevent vblank irq processing while disabling vblank irqs, | ||
120 | * so no updates of timestamps or count can happen after we've | ||
121 | * disabled. Needed to prevent races in case of delayed irq's. | ||
122 | * Disable preemption, so vblank_time_lock is held as short as | ||
123 | * possible, even under a kernel with PREEMPT_RT patches. | ||
124 | */ | ||
125 | preempt_disable(); | ||
126 | spin_lock_irqsave(&dev->vblank_time_lock, irqflags); | ||
127 | |||
128 | dev->driver->disable_vblank(dev, crtc); | ||
129 | dev->vblank_enabled[crtc] = 0; | ||
130 | |||
131 | /* No further vblank irq's will be processed after | ||
132 | * this point. Get current hardware vblank count and | ||
133 | * vblank timestamp, repeat until they are consistent. | ||
134 | * | ||
135 | * FIXME: There is still a race condition here and in | ||
136 | * drm_update_vblank_count() which can cause off-by-one | ||
137 | * reinitialization of software vblank counter. If gpu | ||
138 | * vblank counter doesn't increment exactly at the leading | ||
139 | * edge of a vblank interval, then we can lose 1 count if | ||
140 | * we happen to execute between start of vblank and the | ||
141 | * delayed gpu counter increment. | ||
142 | */ | ||
143 | do { | ||
144 | dev->last_vblank[crtc] = dev->driver->get_vblank_counter(dev, crtc); | ||
145 | vblrc = drm_get_last_vbltimestamp(dev, crtc, &tvblank, 0); | ||
146 | } while (dev->last_vblank[crtc] != dev->driver->get_vblank_counter(dev, crtc)); | ||
147 | |||
148 | /* Compute time difference to stored timestamp of last vblank | ||
149 | * as updated by last invocation of drm_handle_vblank() in vblank irq. | ||
150 | */ | ||
151 | vblcount = atomic_read(&dev->_vblank_count[crtc]); | ||
152 | diff_ns = timeval_to_ns(&tvblank) - | ||
153 | timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount)); | ||
154 | |||
155 | /* If there is at least 1 msec difference between the last stored | ||
156 | * timestamp and tvblank, then we are currently executing our | ||
157 | * disable inside a new vblank interval, the tvblank timestamp | ||
158 | * corresponds to this new vblank interval and the irq handler | ||
159 | * for this vblank didn't run yet and won't run due to our disable. | ||
160 | * Therefore we need to do the job of drm_handle_vblank() and | ||
161 | * increment the vblank counter by one to account for this vblank. | ||
162 | * | ||
163 | * Skip this step if there isn't any high precision timestamp | ||
164 | * available. In that case we can't account for this and just | ||
165 | * hope for the best. | ||
166 | */ | ||
167 | if ((vblrc > 0) && (abs(diff_ns) > 1000000)) | ||
168 | atomic_inc(&dev->_vblank_count[crtc]); | ||
169 | |||
170 | /* Invalidate all timestamps while vblank irq's are off. */ | ||
171 | clear_vblank_timestamps(dev, crtc); | ||
172 | |||
173 | spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); | ||
174 | preempt_enable(); | ||
175 | } | ||
176 | |||
80 | static void vblank_disable_fn(unsigned long arg) | 177 | static void vblank_disable_fn(unsigned long arg) |
81 | { | 178 | { |
82 | struct drm_device *dev = (struct drm_device *)arg; | 179 | struct drm_device *dev = (struct drm_device *)arg; |
@@ -91,10 +188,7 @@ static void vblank_disable_fn(unsigned long arg) | |||
91 | if (atomic_read(&dev->vblank_refcount[i]) == 0 && | 188 | if (atomic_read(&dev->vblank_refcount[i]) == 0 && |
92 | dev->vblank_enabled[i]) { | 189 | dev->vblank_enabled[i]) { |
93 | DRM_DEBUG("disabling vblank on crtc %d\n", i); | 190 | DRM_DEBUG("disabling vblank on crtc %d\n", i); |
94 | dev->last_vblank[i] = | 191 | vblank_disable_and_save(dev, i); |
95 | dev->driver->get_vblank_counter(dev, i); | ||
96 | dev->driver->disable_vblank(dev, i); | ||
97 | dev->vblank_enabled[i] = 0; | ||
98 | } | 192 | } |
99 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | 193 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
100 | } | 194 | } |
@@ -117,6 +211,7 @@ void drm_vblank_cleanup(struct drm_device *dev) | |||
117 | kfree(dev->last_vblank); | 211 | kfree(dev->last_vblank); |
118 | kfree(dev->last_vblank_wait); | 212 | kfree(dev->last_vblank_wait); |
119 | kfree(dev->vblank_inmodeset); | 213 | kfree(dev->vblank_inmodeset); |
214 | kfree(dev->_vblank_time); | ||
120 | 215 | ||
121 | dev->num_crtcs = 0; | 216 | dev->num_crtcs = 0; |
122 | } | 217 | } |
@@ -129,6 +224,8 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) | |||
129 | setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, | 224 | setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, |
130 | (unsigned long)dev); | 225 | (unsigned long)dev); |
131 | spin_lock_init(&dev->vbl_lock); | 226 | spin_lock_init(&dev->vbl_lock); |
227 | spin_lock_init(&dev->vblank_time_lock); | ||
228 | |||
132 | dev->num_crtcs = num_crtcs; | 229 | dev->num_crtcs = num_crtcs; |
133 | 230 | ||
134 | dev->vbl_queue = kmalloc(sizeof(wait_queue_head_t) * num_crtcs, | 231 | dev->vbl_queue = kmalloc(sizeof(wait_queue_head_t) * num_crtcs, |
@@ -161,6 +258,19 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) | |||
161 | if (!dev->vblank_inmodeset) | 258 | if (!dev->vblank_inmodeset) |
162 | goto err; | 259 | goto err; |
163 | 260 | ||
261 | dev->_vblank_time = kcalloc(num_crtcs * DRM_VBLANKTIME_RBSIZE, | ||
262 | sizeof(struct timeval), GFP_KERNEL); | ||
263 | if (!dev->_vblank_time) | ||
264 | goto err; | ||
265 | |||
266 | DRM_INFO("Supports vblank timestamp caching Rev 1 (10.10.2010).\n"); | ||
267 | |||
268 | /* Driver specific high-precision vblank timestamping supported? */ | ||
269 | if (dev->driver->get_vblank_timestamp) | ||
270 | DRM_INFO("Driver supports precise vblank timestamp query.\n"); | ||
271 | else | ||
272 | DRM_INFO("No driver support for vblank timestamp query.\n"); | ||
273 | |||
164 | /* Zero per-crtc vblank stuff */ | 274 | /* Zero per-crtc vblank stuff */ |
165 | for (i = 0; i < num_crtcs; i++) { | 275 | for (i = 0; i < num_crtcs; i++) { |
166 | init_waitqueue_head(&dev->vbl_queue[i]); | 276 | init_waitqueue_head(&dev->vbl_queue[i]); |
@@ -279,7 +389,7 @@ EXPORT_SYMBOL(drm_irq_install); | |||
279 | * | 389 | * |
280 | * Calls the driver's \c drm_driver_irq_uninstall() function, and stops the irq. | 390 | * Calls the driver's \c drm_driver_irq_uninstall() function, and stops the irq. |
281 | */ | 391 | */ |
282 | int drm_irq_uninstall(struct drm_device * dev) | 392 | int drm_irq_uninstall(struct drm_device *dev) |
283 | { | 393 | { |
284 | unsigned long irqflags; | 394 | unsigned long irqflags; |
285 | int irq_enabled, i; | 395 | int irq_enabled, i; |
@@ -335,7 +445,9 @@ int drm_control(struct drm_device *dev, void *data, | |||
335 | { | 445 | { |
336 | struct drm_control *ctl = data; | 446 | struct drm_control *ctl = data; |
337 | 447 | ||
338 | /* if we haven't irq we fallback for compatibility reasons - this used to be a separate function in drm_dma.h */ | 448 | /* if we haven't irq we fallback for compatibility reasons - |
449 | * this used to be a separate function in drm_dma.h | ||
450 | */ | ||
339 | 451 | ||
340 | 452 | ||
341 | switch (ctl->func) { | 453 | switch (ctl->func) { |
@@ -360,6 +472,287 @@ int drm_control(struct drm_device *dev, void *data, | |||
360 | } | 472 | } |
361 | 473 | ||
362 | /** | 474 | /** |
475 | * drm_calc_timestamping_constants - Calculate and | ||
476 | * store various constants which are later needed by | ||
477 | * vblank and swap-completion timestamping, e.g, by | ||
478 | * drm_calc_vbltimestamp_from_scanoutpos(). | ||
479 | * They are derived from crtc's true scanout timing, | ||
480 | * so they take things like panel scaling or other | ||
481 | * adjustments into account. | ||
482 | * | ||
483 | * @crtc drm_crtc whose timestamp constants should be updated. | ||
484 | * | ||
485 | */ | ||
486 | void drm_calc_timestamping_constants(struct drm_crtc *crtc) | ||
487 | { | ||
488 | s64 linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; | ||
489 | u64 dotclock; | ||
490 | |||
491 | /* Dot clock in Hz: */ | ||
492 | dotclock = (u64) crtc->hwmode.clock * 1000; | ||
493 | |||
494 | /* Valid dotclock? */ | ||
495 | if (dotclock > 0) { | ||
496 | /* Convert scanline length in pixels and video dot clock to | ||
497 | * line duration, frame duration and pixel duration in | ||
498 | * nanoseconds: | ||
499 | */ | ||
500 | pixeldur_ns = (s64) div64_u64(1000000000, dotclock); | ||
501 | linedur_ns = (s64) div64_u64(((u64) crtc->hwmode.crtc_htotal * | ||
502 | 1000000000), dotclock); | ||
503 | framedur_ns = (s64) crtc->hwmode.crtc_vtotal * linedur_ns; | ||
504 | } else | ||
505 | DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n", | ||
506 | crtc->base.id); | ||
507 | |||
508 | crtc->pixeldur_ns = pixeldur_ns; | ||
509 | crtc->linedur_ns = linedur_ns; | ||
510 | crtc->framedur_ns = framedur_ns; | ||
511 | |||
512 | DRM_DEBUG("crtc %d: hwmode: htotal %d, vtotal %d, vdisplay %d\n", | ||
513 | crtc->base.id, crtc->hwmode.crtc_htotal, | ||
514 | crtc->hwmode.crtc_vtotal, crtc->hwmode.crtc_vdisplay); | ||
515 | DRM_DEBUG("crtc %d: clock %d kHz framedur %d linedur %d, pixeldur %d\n", | ||
516 | crtc->base.id, (int) dotclock/1000, (int) framedur_ns, | ||
517 | (int) linedur_ns, (int) pixeldur_ns); | ||
518 | } | ||
519 | EXPORT_SYMBOL(drm_calc_timestamping_constants); | ||
520 | |||
521 | /** | ||
522 | * drm_calc_vbltimestamp_from_scanoutpos - helper routine for kms | ||
523 | * drivers. Implements calculation of exact vblank timestamps from | ||
524 | * given drm_display_mode timings and current video scanout position | ||
525 | * of a crtc. This can be called from within get_vblank_timestamp() | ||
526 | * implementation of a kms driver to implement the actual timestamping. | ||
527 | * | ||
528 | * Should return timestamps conforming to the OML_sync_control OpenML | ||
529 | * extension specification. The timestamp corresponds to the end of | ||
530 | * the vblank interval, aka start of scanout of topmost-leftmost display | ||
531 | * pixel in the following video frame. | ||
532 | * | ||
533 | * Requires support for optional dev->driver->get_scanout_position() | ||
534 | * in kms driver, plus a bit of setup code to provide a drm_display_mode | ||
535 | * that corresponds to the true scanout timing. | ||
536 | * | ||
537 | * The current implementation only handles standard video modes. It | ||
538 | * returns as no operation if a doublescan or interlaced video mode is | ||
539 | * active. Higher level code is expected to handle this. | ||
540 | * | ||
541 | * @dev: DRM device. | ||
542 | * @crtc: Which crtc's vblank timestamp to retrieve. | ||
543 | * @max_error: Desired maximum allowable error in timestamps (nanosecs). | ||
544 | * On return contains true maximum error of timestamp. | ||
545 | * @vblank_time: Pointer to struct timeval which should receive the timestamp. | ||
546 | * @flags: Flags to pass to driver: | ||
547 | * 0 = Default. | ||
548 | * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler. | ||
549 | * @refcrtc: drm_crtc* of crtc which defines scanout timing. | ||
550 | * | ||
551 | * Returns negative value on error, failure or if not supported in current | ||
552 | * video mode: | ||
553 | * | ||
554 | * -EINVAL - Invalid crtc. | ||
555 | * -EAGAIN - Temporary unavailable, e.g., called before initial modeset. | ||
556 | * -ENOTSUPP - Function not supported in current display mode. | ||
557 | * -EIO - Failed, e.g., due to failed scanout position query. | ||
558 | * | ||
559 | * Returns or'ed positive status flags on success: | ||
560 | * | ||
561 | * DRM_VBLANKTIME_SCANOUTPOS_METHOD - Signal this method used for timestamping. | ||
562 | * DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval. | ||
563 | * | ||
564 | */ | ||
565 | int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, | ||
566 | int *max_error, | ||
567 | struct timeval *vblank_time, | ||
568 | unsigned flags, | ||
569 | struct drm_crtc *refcrtc) | ||
570 | { | ||
571 | struct timeval stime, raw_time; | ||
572 | struct drm_display_mode *mode; | ||
573 | int vbl_status, vtotal, vdisplay; | ||
574 | int vpos, hpos, i; | ||
575 | s64 framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns; | ||
576 | bool invbl; | ||
577 | |||
578 | if (crtc < 0 || crtc >= dev->num_crtcs) { | ||
579 | DRM_ERROR("Invalid crtc %d\n", crtc); | ||
580 | return -EINVAL; | ||
581 | } | ||
582 | |||
583 | /* Scanout position query not supported? Should not happen. */ | ||
584 | if (!dev->driver->get_scanout_position) { | ||
585 | DRM_ERROR("Called from driver w/o get_scanout_position()!?\n"); | ||
586 | return -EIO; | ||
587 | } | ||
588 | |||
589 | mode = &refcrtc->hwmode; | ||
590 | vtotal = mode->crtc_vtotal; | ||
591 | vdisplay = mode->crtc_vdisplay; | ||
592 | |||
593 | /* Durations of frames, lines, pixels in nanoseconds. */ | ||
594 | framedur_ns = refcrtc->framedur_ns; | ||
595 | linedur_ns = refcrtc->linedur_ns; | ||
596 | pixeldur_ns = refcrtc->pixeldur_ns; | ||
597 | |||
598 | /* If mode timing undefined, just return as no-op: | ||
599 | * Happens during initial modesetting of a crtc. | ||
600 | */ | ||
601 | if (vtotal <= 0 || vdisplay <= 0 || framedur_ns == 0) { | ||
602 | DRM_DEBUG("crtc %d: Noop due to uninitialized mode.\n", crtc); | ||
603 | return -EAGAIN; | ||
604 | } | ||
605 | |||
606 | /* Don't know yet how to handle interlaced or | ||
607 | * double scan modes. Just no-op for now. | ||
608 | */ | ||
609 | if (mode->flags & (DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLSCAN)) { | ||
610 | DRM_DEBUG("crtc %d: Noop due to unsupported mode.\n", crtc); | ||
611 | return -ENOTSUPP; | ||
612 | } | ||
613 | |||
614 | /* Get current scanout position with system timestamp. | ||
615 | * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times | ||
616 | * if single query takes longer than max_error nanoseconds. | ||
617 | * | ||
618 | * This guarantees a tight bound on maximum error if | ||
619 | * code gets preempted or delayed for some reason. | ||
620 | */ | ||
621 | for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) { | ||
622 | /* Disable preemption to make it very likely to | ||
623 | * succeed in the first iteration even on PREEMPT_RT kernel. | ||
624 | */ | ||
625 | preempt_disable(); | ||
626 | |||
627 | /* Get system timestamp before query. */ | ||
628 | do_gettimeofday(&stime); | ||
629 | |||
630 | /* Get vertical and horizontal scanout pos. vpos, hpos. */ | ||
631 | vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos, &hpos); | ||
632 | |||
633 | /* Get system timestamp after query. */ | ||
634 | do_gettimeofday(&raw_time); | ||
635 | |||
636 | preempt_enable(); | ||
637 | |||
638 | /* Return as no-op if scanout query unsupported or failed. */ | ||
639 | if (!(vbl_status & DRM_SCANOUTPOS_VALID)) { | ||
640 | DRM_DEBUG("crtc %d : scanoutpos query failed [%d].\n", | ||
641 | crtc, vbl_status); | ||
642 | return -EIO; | ||
643 | } | ||
644 | |||
645 | duration_ns = timeval_to_ns(&raw_time) - timeval_to_ns(&stime); | ||
646 | |||
647 | /* Accept result with < max_error nsecs timing uncertainty. */ | ||
648 | if (duration_ns <= (s64) *max_error) | ||
649 | break; | ||
650 | } | ||
651 | |||
652 | /* Noisy system timing? */ | ||
653 | if (i == DRM_TIMESTAMP_MAXRETRIES) { | ||
654 | DRM_DEBUG("crtc %d: Noisy timestamp %d us > %d us [%d reps].\n", | ||
655 | crtc, (int) duration_ns/1000, *max_error/1000, i); | ||
656 | } | ||
657 | |||
658 | /* Return upper bound of timestamp precision error. */ | ||
659 | *max_error = (int) duration_ns; | ||
660 | |||
661 | /* Check if in vblank area: | ||
662 | * vpos is >=0 in video scanout area, but negative | ||
663 | * within vblank area, counting down the number of lines until | ||
664 | * start of scanout. | ||
665 | */ | ||
666 | invbl = vbl_status & DRM_SCANOUTPOS_INVBL; | ||
667 | |||
668 | /* Convert scanout position into elapsed time at raw_time query | ||
669 | * since start of scanout at first display scanline. delta_ns | ||
670 | * can be negative if start of scanout hasn't happened yet. | ||
671 | */ | ||
672 | delta_ns = (s64) vpos * linedur_ns + (s64) hpos * pixeldur_ns; | ||
673 | |||
674 | /* Is vpos outside nominal vblank area, but less than | ||
675 | * 1/100 of a frame height away from start of vblank? | ||
676 | * If so, assume this isn't a massively delayed vblank | ||
677 | * interrupt, but a vblank interrupt that fired a few | ||
678 | * microseconds before true start of vblank. Compensate | ||
679 | * by adding a full frame duration to the final timestamp. | ||
680 | * Happens, e.g., on ATI R500, R600. | ||
681 | * | ||
682 | * We only do this if DRM_CALLED_FROM_VBLIRQ. | ||
683 | */ | ||
684 | if ((flags & DRM_CALLED_FROM_VBLIRQ) && !invbl && | ||
685 | ((vdisplay - vpos) < vtotal / 100)) { | ||
686 | delta_ns = delta_ns - framedur_ns; | ||
687 | |||
688 | /* Signal this correction as "applied". */ | ||
689 | vbl_status |= 0x8; | ||
690 | } | ||
691 | |||
692 | /* Subtract time delta from raw timestamp to get final | ||
693 | * vblank_time timestamp for end of vblank. | ||
694 | */ | ||
695 | *vblank_time = ns_to_timeval(timeval_to_ns(&raw_time) - delta_ns); | ||
696 | |||
697 | DRM_DEBUG("crtc %d : v %d p(%d,%d)@ %d.%d -> %d.%d [e %d us, %d rep]\n", | ||
698 | crtc, (int) vbl_status, hpos, vpos, raw_time.tv_sec, | ||
699 | raw_time.tv_usec, vblank_time->tv_sec, vblank_time->tv_usec, | ||
700 | (int) duration_ns/1000, i); | ||
701 | |||
702 | vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD; | ||
703 | if (invbl) | ||
704 | vbl_status |= DRM_VBLANKTIME_INVBL; | ||
705 | |||
706 | return vbl_status; | ||
707 | } | ||
708 | EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); | ||
709 | |||
710 | /** | ||
711 | * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent | ||
712 | * vblank interval. | ||
713 | * | ||
714 | * @dev: DRM device | ||
715 | * @crtc: which crtc's vblank timestamp to retrieve | ||
716 | * @tvblank: Pointer to target struct timeval which should receive the timestamp | ||
717 | * @flags: Flags to pass to driver: | ||
718 | * 0 = Default. | ||
719 | * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler. | ||
720 | * | ||
721 | * Fetches the system timestamp corresponding to the time of the most recent | ||
722 | * vblank interval on specified crtc. May call into kms-driver to | ||
723 | * compute the timestamp with a high-precision GPU specific method. | ||
724 | * | ||
725 | * Returns zero if timestamp originates from uncorrected do_gettimeofday() | ||
726 | * call, i.e., it isn't very precisely locked to the true vblank. | ||
727 | * | ||
728 | * Returns non-zero if timestamp is considered to be very precise. | ||
729 | */ | ||
730 | u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, | ||
731 | struct timeval *tvblank, unsigned flags) | ||
732 | { | ||
733 | int ret = 0; | ||
734 | |||
735 | /* Define requested maximum error on timestamps (nanoseconds). */ | ||
736 | int max_error = (int) drm_timestamp_precision * 1000; | ||
737 | |||
738 | /* Query driver if possible and precision timestamping enabled. */ | ||
739 | if (dev->driver->get_vblank_timestamp && (max_error > 0)) { | ||
740 | ret = dev->driver->get_vblank_timestamp(dev, crtc, &max_error, | ||
741 | tvblank, flags); | ||
742 | if (ret > 0) | ||
743 | return (u32) ret; | ||
744 | } | ||
745 | |||
746 | /* GPU high precision timestamp query unsupported or failed. | ||
747 | * Return gettimeofday timestamp as best estimate. | ||
748 | */ | ||
749 | do_gettimeofday(tvblank); | ||
750 | |||
751 | return 0; | ||
752 | } | ||
753 | EXPORT_SYMBOL(drm_get_last_vbltimestamp); | ||
754 | |||
755 | /** | ||
363 | * drm_vblank_count - retrieve "cooked" vblank counter value | 756 | * drm_vblank_count - retrieve "cooked" vblank counter value |
364 | * @dev: DRM device | 757 | * @dev: DRM device |
365 | * @crtc: which counter to retrieve | 758 | * @crtc: which counter to retrieve |
@@ -375,6 +768,40 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc) | |||
375 | EXPORT_SYMBOL(drm_vblank_count); | 768 | EXPORT_SYMBOL(drm_vblank_count); |
376 | 769 | ||
377 | /** | 770 | /** |
771 | * drm_vblank_count_and_time - retrieve "cooked" vblank counter value | ||
772 | * and the system timestamp corresponding to that vblank counter value. | ||
773 | * | ||
774 | * @dev: DRM device | ||
775 | * @crtc: which counter to retrieve | ||
776 | * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. | ||
777 | * | ||
778 | * Fetches the "cooked" vblank count value that represents the number of | ||
779 | * vblank events since the system was booted, including lost events due to | ||
780 | * modesetting activity. Returns corresponding system timestamp of the time | ||
781 | * of the vblank interval that corresponds to the current value vblank counter | ||
782 | * value. | ||
783 | */ | ||
784 | u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, | ||
785 | struct timeval *vblanktime) | ||
786 | { | ||
787 | u32 cur_vblank; | ||
788 | |||
789 | /* Read timestamp from slot of _vblank_time ringbuffer | ||
790 | * that corresponds to current vblank count. Retry if | ||
791 | * count has incremented during readout. This works like | ||
792 | * a seqlock. | ||
793 | */ | ||
794 | do { | ||
795 | cur_vblank = atomic_read(&dev->_vblank_count[crtc]); | ||
796 | *vblanktime = vblanktimestamp(dev, crtc, cur_vblank); | ||
797 | smp_rmb(); | ||
798 | } while (cur_vblank != atomic_read(&dev->_vblank_count[crtc])); | ||
799 | |||
800 | return cur_vblank; | ||
801 | } | ||
802 | EXPORT_SYMBOL(drm_vblank_count_and_time); | ||
803 | |||
804 | /** | ||
378 | * drm_update_vblank_count - update the master vblank counter | 805 | * drm_update_vblank_count - update the master vblank counter |
379 | * @dev: DRM device | 806 | * @dev: DRM device |
380 | * @crtc: counter to update | 807 | * @crtc: counter to update |
@@ -392,7 +819,8 @@ EXPORT_SYMBOL(drm_vblank_count); | |||
392 | */ | 819 | */ |
393 | static void drm_update_vblank_count(struct drm_device *dev, int crtc) | 820 | static void drm_update_vblank_count(struct drm_device *dev, int crtc) |
394 | { | 821 | { |
395 | u32 cur_vblank, diff; | 822 | u32 cur_vblank, diff, tslot, rc; |
823 | struct timeval t_vblank; | ||
396 | 824 | ||
397 | /* | 825 | /* |
398 | * Interrupts were disabled prior to this call, so deal with counter | 826 | * Interrupts were disabled prior to this call, so deal with counter |
@@ -400,8 +828,18 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) | |||
400 | * NOTE! It's possible we lost a full dev->max_vblank_count events | 828 | * NOTE! It's possible we lost a full dev->max_vblank_count events |
401 | * here if the register is small or we had vblank interrupts off for | 829 | * here if the register is small or we had vblank interrupts off for |
402 | * a long time. | 830 | * a long time. |
831 | * | ||
832 | * We repeat the hardware vblank counter & timestamp query until | ||
833 | * we get consistent results. This to prevent races between gpu | ||
834 | * updating its hardware counter while we are retrieving the | ||
835 | * corresponding vblank timestamp. | ||
403 | */ | 836 | */ |
404 | cur_vblank = dev->driver->get_vblank_counter(dev, crtc); | 837 | do { |
838 | cur_vblank = dev->driver->get_vblank_counter(dev, crtc); | ||
839 | rc = drm_get_last_vbltimestamp(dev, crtc, &t_vblank, 0); | ||
840 | } while (cur_vblank != dev->driver->get_vblank_counter(dev, crtc)); | ||
841 | |||
842 | /* Deal with counter wrap */ | ||
405 | diff = cur_vblank - dev->last_vblank[crtc]; | 843 | diff = cur_vblank - dev->last_vblank[crtc]; |
406 | if (cur_vblank < dev->last_vblank[crtc]) { | 844 | if (cur_vblank < dev->last_vblank[crtc]) { |
407 | diff += dev->max_vblank_count; | 845 | diff += dev->max_vblank_count; |
@@ -413,6 +851,16 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) | |||
413 | DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n", | 851 | DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n", |
414 | crtc, diff); | 852 | crtc, diff); |
415 | 853 | ||
854 | /* Reinitialize corresponding vblank timestamp if high-precision query | ||
855 | * available. Skip this step if query unsupported or failed. Will | ||
856 | * reinitialize delayed at next vblank interrupt in that case. | ||
857 | */ | ||
858 | if (rc) { | ||
859 | tslot = atomic_read(&dev->_vblank_count[crtc]) + diff; | ||
860 | vblanktimestamp(dev, crtc, tslot) = t_vblank; | ||
861 | smp_wmb(); | ||
862 | } | ||
863 | |||
416 | atomic_add(diff, &dev->_vblank_count[crtc]); | 864 | atomic_add(diff, &dev->_vblank_count[crtc]); |
417 | } | 865 | } |
418 | 866 | ||
@@ -429,15 +877,27 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) | |||
429 | */ | 877 | */ |
430 | int drm_vblank_get(struct drm_device *dev, int crtc) | 878 | int drm_vblank_get(struct drm_device *dev, int crtc) |
431 | { | 879 | { |
432 | unsigned long irqflags; | 880 | unsigned long irqflags, irqflags2; |
433 | int ret = 0; | 881 | int ret = 0; |
434 | 882 | ||
435 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 883 | spin_lock_irqsave(&dev->vbl_lock, irqflags); |
436 | /* Going from 0->1 means we have to enable interrupts again */ | 884 | /* Going from 0->1 means we have to enable interrupts again */ |
437 | if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) { | 885 | if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) { |
886 | /* Disable preemption while holding vblank_time_lock. Do | ||
887 | * it explicitely to guard against PREEMPT_RT kernel. | ||
888 | */ | ||
889 | preempt_disable(); | ||
890 | spin_lock_irqsave(&dev->vblank_time_lock, irqflags2); | ||
438 | if (!dev->vblank_enabled[crtc]) { | 891 | if (!dev->vblank_enabled[crtc]) { |
892 | /* Enable vblank irqs under vblank_time_lock protection. | ||
893 | * All vblank count & timestamp updates are held off | ||
894 | * until we are done reinitializing master counter and | ||
895 | * timestamps. Filtercode in drm_handle_vblank() will | ||
896 | * prevent double-accounting of same vblank interval. | ||
897 | */ | ||
439 | ret = dev->driver->enable_vblank(dev, crtc); | 898 | ret = dev->driver->enable_vblank(dev, crtc); |
440 | DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret); | 899 | DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", |
900 | crtc, ret); | ||
441 | if (ret) | 901 | if (ret) |
442 | atomic_dec(&dev->vblank_refcount[crtc]); | 902 | atomic_dec(&dev->vblank_refcount[crtc]); |
443 | else { | 903 | else { |
@@ -445,6 +905,8 @@ int drm_vblank_get(struct drm_device *dev, int crtc) | |||
445 | drm_update_vblank_count(dev, crtc); | 905 | drm_update_vblank_count(dev, crtc); |
446 | } | 906 | } |
447 | } | 907 | } |
908 | spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags2); | ||
909 | preempt_enable(); | ||
448 | } else { | 910 | } else { |
449 | if (!dev->vblank_enabled[crtc]) { | 911 | if (!dev->vblank_enabled[crtc]) { |
450 | atomic_dec(&dev->vblank_refcount[crtc]); | 912 | atomic_dec(&dev->vblank_refcount[crtc]); |
@@ -463,15 +925,17 @@ EXPORT_SYMBOL(drm_vblank_get); | |||
463 | * @crtc: which counter to give up | 925 | * @crtc: which counter to give up |
464 | * | 926 | * |
465 | * Release ownership of a given vblank counter, turning off interrupts | 927 | * Release ownership of a given vblank counter, turning off interrupts |
466 | * if possible. | 928 | * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. |
467 | */ | 929 | */ |
468 | void drm_vblank_put(struct drm_device *dev, int crtc) | 930 | void drm_vblank_put(struct drm_device *dev, int crtc) |
469 | { | 931 | { |
470 | BUG_ON (atomic_read (&dev->vblank_refcount[crtc]) == 0); | 932 | BUG_ON(atomic_read(&dev->vblank_refcount[crtc]) == 0); |
471 | 933 | ||
472 | /* Last user schedules interrupt disable */ | 934 | /* Last user schedules interrupt disable */ |
473 | if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) | 935 | if (atomic_dec_and_test(&dev->vblank_refcount[crtc]) && |
474 | mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ); | 936 | (drm_vblank_offdelay > 0)) |
937 | mod_timer(&dev->vblank_disable_timer, | ||
938 | jiffies + ((drm_vblank_offdelay * DRM_HZ)/1000)); | ||
475 | } | 939 | } |
476 | EXPORT_SYMBOL(drm_vblank_put); | 940 | EXPORT_SYMBOL(drm_vblank_put); |
477 | 941 | ||
@@ -480,10 +944,8 @@ void drm_vblank_off(struct drm_device *dev, int crtc) | |||
480 | unsigned long irqflags; | 944 | unsigned long irqflags; |
481 | 945 | ||
482 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 946 | spin_lock_irqsave(&dev->vbl_lock, irqflags); |
483 | dev->driver->disable_vblank(dev, crtc); | 947 | vblank_disable_and_save(dev, crtc); |
484 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | 948 | DRM_WAKEUP(&dev->vbl_queue[crtc]); |
485 | dev->vblank_enabled[crtc] = 0; | ||
486 | dev->last_vblank[crtc] = dev->driver->get_vblank_counter(dev, crtc); | ||
487 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | 949 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
488 | } | 950 | } |
489 | EXPORT_SYMBOL(drm_vblank_off); | 951 | EXPORT_SYMBOL(drm_vblank_off); |
@@ -602,7 +1064,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe, | |||
602 | e->base.file_priv = file_priv; | 1064 | e->base.file_priv = file_priv; |
603 | e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; | 1065 | e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; |
604 | 1066 | ||
605 | do_gettimeofday(&now); | ||
606 | spin_lock_irqsave(&dev->event_lock, flags); | 1067 | spin_lock_irqsave(&dev->event_lock, flags); |
607 | 1068 | ||
608 | if (file_priv->event_space < sizeof e->event) { | 1069 | if (file_priv->event_space < sizeof e->event) { |
@@ -611,7 +1072,8 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe, | |||
611 | } | 1072 | } |
612 | 1073 | ||
613 | file_priv->event_space -= sizeof e->event; | 1074 | file_priv->event_space -= sizeof e->event; |
614 | seq = drm_vblank_count(dev, pipe); | 1075 | seq = drm_vblank_count_and_time(dev, pipe, &now); |
1076 | |||
615 | if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) && | 1077 | if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) && |
616 | (seq - vblwait->request.sequence) <= (1 << 23)) { | 1078 | (seq - vblwait->request.sequence) <= (1 << 23)) { |
617 | vblwait->request.sequence = seq + 1; | 1079 | vblwait->request.sequence = seq + 1; |
@@ -626,15 +1088,18 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe, | |||
626 | 1088 | ||
627 | e->event.sequence = vblwait->request.sequence; | 1089 | e->event.sequence = vblwait->request.sequence; |
628 | if ((seq - vblwait->request.sequence) <= (1 << 23)) { | 1090 | if ((seq - vblwait->request.sequence) <= (1 << 23)) { |
1091 | e->event.sequence = seq; | ||
629 | e->event.tv_sec = now.tv_sec; | 1092 | e->event.tv_sec = now.tv_sec; |
630 | e->event.tv_usec = now.tv_usec; | 1093 | e->event.tv_usec = now.tv_usec; |
631 | drm_vblank_put(dev, pipe); | 1094 | drm_vblank_put(dev, pipe); |
632 | list_add_tail(&e->base.link, &e->base.file_priv->event_list); | 1095 | list_add_tail(&e->base.link, &e->base.file_priv->event_list); |
633 | wake_up_interruptible(&e->base.file_priv->event_wait); | 1096 | wake_up_interruptible(&e->base.file_priv->event_wait); |
1097 | vblwait->reply.sequence = seq; | ||
634 | trace_drm_vblank_event_delivered(current->pid, pipe, | 1098 | trace_drm_vblank_event_delivered(current->pid, pipe, |
635 | vblwait->request.sequence); | 1099 | vblwait->request.sequence); |
636 | } else { | 1100 | } else { |
637 | list_add_tail(&e->base.link, &dev->vblank_event_list); | 1101 | list_add_tail(&e->base.link, &dev->vblank_event_list); |
1102 | vblwait->reply.sequence = vblwait->request.sequence; | ||
638 | } | 1103 | } |
639 | 1104 | ||
640 | spin_unlock_irqrestore(&dev->event_lock, flags); | 1105 | spin_unlock_irqrestore(&dev->event_lock, flags); |
@@ -727,11 +1192,10 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
727 | if (ret != -EINTR) { | 1192 | if (ret != -EINTR) { |
728 | struct timeval now; | 1193 | struct timeval now; |
729 | 1194 | ||
730 | do_gettimeofday(&now); | 1195 | vblwait->reply.sequence = drm_vblank_count_and_time(dev, crtc, &now); |
731 | |||
732 | vblwait->reply.tval_sec = now.tv_sec; | 1196 | vblwait->reply.tval_sec = now.tv_sec; |
733 | vblwait->reply.tval_usec = now.tv_usec; | 1197 | vblwait->reply.tval_usec = now.tv_usec; |
734 | vblwait->reply.sequence = drm_vblank_count(dev, crtc); | 1198 | |
735 | DRM_DEBUG("returning %d to client\n", | 1199 | DRM_DEBUG("returning %d to client\n", |
736 | vblwait->reply.sequence); | 1200 | vblwait->reply.sequence); |
737 | } else { | 1201 | } else { |
@@ -750,8 +1214,7 @@ void drm_handle_vblank_events(struct drm_device *dev, int crtc) | |||
750 | unsigned long flags; | 1214 | unsigned long flags; |
751 | unsigned int seq; | 1215 | unsigned int seq; |
752 | 1216 | ||
753 | do_gettimeofday(&now); | 1217 | seq = drm_vblank_count_and_time(dev, crtc, &now); |
754 | seq = drm_vblank_count(dev, crtc); | ||
755 | 1218 | ||
756 | spin_lock_irqsave(&dev->event_lock, flags); | 1219 | spin_lock_irqsave(&dev->event_lock, flags); |
757 | 1220 | ||
@@ -789,11 +1252,64 @@ void drm_handle_vblank_events(struct drm_device *dev, int crtc) | |||
789 | */ | 1252 | */ |
790 | void drm_handle_vblank(struct drm_device *dev, int crtc) | 1253 | void drm_handle_vblank(struct drm_device *dev, int crtc) |
791 | { | 1254 | { |
1255 | u32 vblcount; | ||
1256 | s64 diff_ns; | ||
1257 | struct timeval tvblank; | ||
1258 | unsigned long irqflags; | ||
1259 | |||
792 | if (!dev->num_crtcs) | 1260 | if (!dev->num_crtcs) |
793 | return; | 1261 | return; |
794 | 1262 | ||
795 | atomic_inc(&dev->_vblank_count[crtc]); | 1263 | /* Need timestamp lock to prevent concurrent execution with |
1264 | * vblank enable/disable, as this would cause inconsistent | ||
1265 | * or corrupted timestamps and vblank counts. | ||
1266 | */ | ||
1267 | spin_lock_irqsave(&dev->vblank_time_lock, irqflags); | ||
1268 | |||
1269 | /* Vblank irq handling disabled. Nothing to do. */ | ||
1270 | if (!dev->vblank_enabled[crtc]) { | ||
1271 | spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); | ||
1272 | return; | ||
1273 | } | ||
1274 | |||
1275 | /* Fetch corresponding timestamp for this vblank interval from | ||
1276 | * driver and store it in proper slot of timestamp ringbuffer. | ||
1277 | */ | ||
1278 | |||
1279 | /* Get current timestamp and count. */ | ||
1280 | vblcount = atomic_read(&dev->_vblank_count[crtc]); | ||
1281 | drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ); | ||
1282 | |||
1283 | /* Compute time difference to timestamp of last vblank */ | ||
1284 | diff_ns = timeval_to_ns(&tvblank) - | ||
1285 | timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount)); | ||
1286 | |||
1287 | /* Update vblank timestamp and count if at least | ||
1288 | * DRM_REDUNDANT_VBLIRQ_THRESH_NS nanoseconds | ||
1289 | * difference between last stored timestamp and current | ||
1290 | * timestamp. A smaller difference means basically | ||
1291 | * identical timestamps. Happens if this vblank has | ||
1292 | * been already processed and this is a redundant call, | ||
1293 | * e.g., due to spurious vblank interrupts. We need to | ||
1294 | * ignore those for accounting. | ||
1295 | */ | ||
1296 | if (abs(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) { | ||
1297 | /* Store new timestamp in ringbuffer. */ | ||
1298 | vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; | ||
1299 | smp_wmb(); | ||
1300 | |||
1301 | /* Increment cooked vblank count. This also atomically commits | ||
1302 | * the timestamp computed above. | ||
1303 | */ | ||
1304 | atomic_inc(&dev->_vblank_count[crtc]); | ||
1305 | } else { | ||
1306 | DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", | ||
1307 | crtc, (int) diff_ns); | ||
1308 | } | ||
1309 | |||
796 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | 1310 | DRM_WAKEUP(&dev->vbl_queue[crtc]); |
797 | drm_handle_vblank_events(dev, crtc); | 1311 | drm_handle_vblank_events(dev, crtc); |
1312 | |||
1313 | spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); | ||
798 | } | 1314 | } |
799 | EXPORT_SYMBOL(drm_handle_vblank); | 1315 | EXPORT_SYMBOL(drm_handle_vblank); |