diff options
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 173 |
1 files changed, 35 insertions, 138 deletions
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 724e505873cf..69aa0ab28403 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -106,8 +106,6 @@ void drm_vblank_cleanup(struct drm_device *dev) | |||
106 | 106 | ||
107 | drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, | 107 | drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, |
108 | DRM_MEM_DRIVER); | 108 | DRM_MEM_DRIVER); |
109 | drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, | ||
110 | DRM_MEM_DRIVER); | ||
111 | drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * | 109 | drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * |
112 | dev->num_crtcs, DRM_MEM_DRIVER); | 110 | dev->num_crtcs, DRM_MEM_DRIVER); |
113 | drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * | 111 | drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * |
@@ -132,7 +130,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) | |||
132 | setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, | 130 | setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, |
133 | (unsigned long)dev); | 131 | (unsigned long)dev); |
134 | spin_lock_init(&dev->vbl_lock); | 132 | spin_lock_init(&dev->vbl_lock); |
135 | atomic_set(&dev->vbl_signal_pending, 0); | ||
136 | dev->num_crtcs = num_crtcs; | 133 | dev->num_crtcs = num_crtcs; |
137 | 134 | ||
138 | dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs, | 135 | dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs, |
@@ -140,11 +137,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) | |||
140 | if (!dev->vbl_queue) | 137 | if (!dev->vbl_queue) |
141 | goto err; | 138 | goto err; |
142 | 139 | ||
143 | dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs, | ||
144 | DRM_MEM_DRIVER); | ||
145 | if (!dev->vbl_sigs) | ||
146 | goto err; | ||
147 | |||
148 | dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, | 140 | dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, |
149 | DRM_MEM_DRIVER); | 141 | DRM_MEM_DRIVER); |
150 | if (!dev->_vblank_count) | 142 | if (!dev->_vblank_count) |
@@ -177,7 +169,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) | |||
177 | /* Zero per-crtc vblank stuff */ | 169 | /* Zero per-crtc vblank stuff */ |
178 | for (i = 0; i < num_crtcs; i++) { | 170 | for (i = 0; i < num_crtcs; i++) { |
179 | init_waitqueue_head(&dev->vbl_queue[i]); | 171 | init_waitqueue_head(&dev->vbl_queue[i]); |
180 | INIT_LIST_HEAD(&dev->vbl_sigs[i]); | ||
181 | atomic_set(&dev->_vblank_count[i], 0); | 172 | atomic_set(&dev->_vblank_count[i], 0); |
182 | atomic_set(&dev->vblank_refcount[i], 0); | 173 | atomic_set(&dev->vblank_refcount[i], 0); |
183 | } | 174 | } |
@@ -267,7 +258,8 @@ EXPORT_SYMBOL(drm_irq_install); | |||
267 | */ | 258 | */ |
268 | int drm_irq_uninstall(struct drm_device * dev) | 259 | int drm_irq_uninstall(struct drm_device * dev) |
269 | { | 260 | { |
270 | int irq_enabled; | 261 | unsigned long irqflags; |
262 | int irq_enabled, i; | ||
271 | 263 | ||
272 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) | 264 | if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) |
273 | return -EINVAL; | 265 | return -EINVAL; |
@@ -277,6 +269,16 @@ int drm_irq_uninstall(struct drm_device * dev) | |||
277 | dev->irq_enabled = 0; | 269 | dev->irq_enabled = 0; |
278 | mutex_unlock(&dev->struct_mutex); | 270 | mutex_unlock(&dev->struct_mutex); |
279 | 271 | ||
272 | /* | ||
273 | * Wake up any waiters so they don't hang. | ||
274 | */ | ||
275 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
276 | for (i = 0; i < dev->num_crtcs; i++) { | ||
277 | DRM_WAKEUP(&dev->vbl_queue[i]); | ||
278 | dev->vblank_enabled[i] = 0; | ||
279 | } | ||
280 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
281 | |||
280 | if (!irq_enabled) | 282 | if (!irq_enabled) |
281 | return -EINVAL; | 283 | return -EINVAL; |
282 | 284 | ||
@@ -529,15 +531,10 @@ out: | |||
529 | * \param data user argument, pointing to a drm_wait_vblank structure. | 531 | * \param data user argument, pointing to a drm_wait_vblank structure. |
530 | * \return zero on success or a negative number on failure. | 532 | * \return zero on success or a negative number on failure. |
531 | * | 533 | * |
532 | * Verifies the IRQ is installed. | 534 | * This function enables the vblank interrupt on the pipe requested, then |
533 | * | 535 | * sleeps waiting for the requested sequence number to occur, and drops |
534 | * If a signal is requested checks if this task has already scheduled the same signal | 536 | * the vblank interrupt refcount afterwards. (vblank irq disable follows that |
535 | * for the same vblank sequence number - nothing to be done in | 537 | * after a timeout with no further vblank waits scheduled). |
536 | * that case. If the number of tasks waiting for the interrupt exceeds 100 the | ||
537 | * function fails. Otherwise adds a new entry to drm_device::vbl_sigs for this | ||
538 | * task. | ||
539 | * | ||
540 | * If a signal is not requested, then calls vblank_wait(). | ||
541 | */ | 538 | */ |
542 | int drm_wait_vblank(struct drm_device *dev, void *data, | 539 | int drm_wait_vblank(struct drm_device *dev, void *data, |
543 | struct drm_file *file_priv) | 540 | struct drm_file *file_priv) |
@@ -549,6 +546,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
549 | if ((!dev->pdev->irq) || (!dev->irq_enabled)) | 546 | if ((!dev->pdev->irq) || (!dev->irq_enabled)) |
550 | return -EINVAL; | 547 | return -EINVAL; |
551 | 548 | ||
549 | if (vblwait->request.type & _DRM_VBLANK_SIGNAL) | ||
550 | return -EINVAL; | ||
551 | |||
552 | if (vblwait->request.type & | 552 | if (vblwait->request.type & |
553 | ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { | 553 | ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { |
554 | DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", | 554 | DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", |
@@ -586,88 +586,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
586 | vblwait->request.sequence = seq + 1; | 586 | vblwait->request.sequence = seq + 1; |
587 | } | 587 | } |
588 | 588 | ||
589 | if (flags & _DRM_VBLANK_SIGNAL) { | 589 | DRM_DEBUG("waiting on vblank count %d, crtc %d\n", |
590 | unsigned long irqflags; | 590 | vblwait->request.sequence, crtc); |
591 | struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; | 591 | dev->last_vblank_wait[crtc] = vblwait->request.sequence; |
592 | struct drm_vbl_sig *vbl_sig; | 592 | DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, |
593 | 593 | (((drm_vblank_count(dev, crtc) - | |
594 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 594 | vblwait->request.sequence) <= (1 << 23)) || |
595 | 595 | !dev->irq_enabled)); | |
596 | /* Check if this task has already scheduled the same signal | ||
597 | * for the same vblank sequence number; nothing to be done in | ||
598 | * that case | ||
599 | */ | ||
600 | list_for_each_entry(vbl_sig, vbl_sigs, head) { | ||
601 | if (vbl_sig->sequence == vblwait->request.sequence | ||
602 | && vbl_sig->info.si_signo == | ||
603 | vblwait->request.signal | ||
604 | && vbl_sig->task == current) { | ||
605 | spin_unlock_irqrestore(&dev->vbl_lock, | ||
606 | irqflags); | ||
607 | vblwait->reply.sequence = seq; | ||
608 | goto done; | ||
609 | } | ||
610 | } | ||
611 | |||
612 | if (atomic_read(&dev->vbl_signal_pending) >= 100) { | ||
613 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
614 | ret = -EBUSY; | ||
615 | goto done; | ||
616 | } | ||
617 | |||
618 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
619 | |||
620 | vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig), | ||
621 | DRM_MEM_DRIVER); | ||
622 | if (!vbl_sig) { | ||
623 | ret = -ENOMEM; | ||
624 | goto done; | ||
625 | } | ||
626 | |||
627 | /* Get a refcount on the vblank, which will be released by | ||
628 | * drm_vbl_send_signals(). | ||
629 | */ | ||
630 | ret = drm_vblank_get(dev, crtc); | ||
631 | if (ret) { | ||
632 | drm_free(vbl_sig, sizeof(struct drm_vbl_sig), | ||
633 | DRM_MEM_DRIVER); | ||
634 | goto done; | ||
635 | } | ||
636 | |||
637 | atomic_inc(&dev->vbl_signal_pending); | ||
638 | 596 | ||
639 | vbl_sig->sequence = vblwait->request.sequence; | 597 | if (ret != -EINTR) { |
640 | vbl_sig->info.si_signo = vblwait->request.signal; | 598 | struct timeval now; |
641 | vbl_sig->task = current; | ||
642 | 599 | ||
643 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 600 | do_gettimeofday(&now); |
644 | |||
645 | list_add_tail(&vbl_sig->head, vbl_sigs); | ||
646 | |||
647 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
648 | 601 | ||
649 | vblwait->reply.sequence = seq; | 602 | vblwait->reply.tval_sec = now.tv_sec; |
603 | vblwait->reply.tval_usec = now.tv_usec; | ||
604 | vblwait->reply.sequence = drm_vblank_count(dev, crtc); | ||
605 | DRM_DEBUG("returning %d to client\n", | ||
606 | vblwait->reply.sequence); | ||
650 | } else { | 607 | } else { |
651 | DRM_DEBUG("waiting on vblank count %d, crtc %d\n", | 608 | DRM_DEBUG("vblank wait interrupted by signal\n"); |
652 | vblwait->request.sequence, crtc); | ||
653 | dev->last_vblank_wait[crtc] = vblwait->request.sequence; | ||
654 | DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, | ||
655 | ((drm_vblank_count(dev, crtc) | ||
656 | - vblwait->request.sequence) <= (1 << 23))); | ||
657 | |||
658 | if (ret != -EINTR) { | ||
659 | struct timeval now; | ||
660 | |||
661 | do_gettimeofday(&now); | ||
662 | |||
663 | vblwait->reply.tval_sec = now.tv_sec; | ||
664 | vblwait->reply.tval_usec = now.tv_usec; | ||
665 | vblwait->reply.sequence = drm_vblank_count(dev, crtc); | ||
666 | DRM_DEBUG("returning %d to client\n", | ||
667 | vblwait->reply.sequence); | ||
668 | } else { | ||
669 | DRM_DEBUG("vblank wait interrupted by signal\n"); | ||
670 | } | ||
671 | } | 609 | } |
672 | 610 | ||
673 | done: | 611 | done: |
@@ -676,46 +614,6 @@ done: | |||
676 | } | 614 | } |
677 | 615 | ||
678 | /** | 616 | /** |
679 | * Send the VBLANK signals. | ||
680 | * | ||
681 | * \param dev DRM device. | ||
682 | * \param crtc CRTC where the vblank event occurred | ||
683 | * | ||
684 | * Sends a signal for each task in drm_device::vbl_sigs and empties the list. | ||
685 | * | ||
686 | * If a signal is not requested, then calls vblank_wait(). | ||
687 | */ | ||
688 | static void drm_vbl_send_signals(struct drm_device *dev, int crtc) | ||
689 | { | ||
690 | struct drm_vbl_sig *vbl_sig, *tmp; | ||
691 | struct list_head *vbl_sigs; | ||
692 | unsigned int vbl_seq; | ||
693 | unsigned long flags; | ||
694 | |||
695 | spin_lock_irqsave(&dev->vbl_lock, flags); | ||
696 | |||
697 | vbl_sigs = &dev->vbl_sigs[crtc]; | ||
698 | vbl_seq = drm_vblank_count(dev, crtc); | ||
699 | |||
700 | list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { | ||
701 | if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { | ||
702 | vbl_sig->info.si_code = vbl_seq; | ||
703 | send_sig_info(vbl_sig->info.si_signo, | ||
704 | &vbl_sig->info, vbl_sig->task); | ||
705 | |||
706 | list_del(&vbl_sig->head); | ||
707 | |||
708 | drm_free(vbl_sig, sizeof(*vbl_sig), | ||
709 | DRM_MEM_DRIVER); | ||
710 | atomic_dec(&dev->vbl_signal_pending); | ||
711 | drm_vblank_put(dev, crtc); | ||
712 | } | ||
713 | } | ||
714 | |||
715 | spin_unlock_irqrestore(&dev->vbl_lock, flags); | ||
716 | } | ||
717 | |||
718 | /** | ||
719 | * drm_handle_vblank - handle a vblank event | 617 | * drm_handle_vblank - handle a vblank event |
720 | * @dev: DRM device | 618 | * @dev: DRM device |
721 | * @crtc: where this event occurred | 619 | * @crtc: where this event occurred |
@@ -727,6 +625,5 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) | |||
727 | { | 625 | { |
728 | atomic_inc(&dev->_vblank_count[crtc]); | 626 | atomic_inc(&dev->_vblank_count[crtc]); |
729 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | 627 | DRM_WAKEUP(&dev->vbl_queue[crtc]); |
730 | drm_vbl_send_signals(dev, crtc); | ||
731 | } | 628 | } |
732 | EXPORT_SYMBOL(drm_handle_vblank); | 629 | EXPORT_SYMBOL(drm_handle_vblank); |