diff options
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 161 |
1 files changed, 23 insertions, 138 deletions
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 477caa1b1e4b..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 | } |
@@ -540,15 +531,10 @@ out: | |||
540 | * \param data user argument, pointing to a drm_wait_vblank structure. | 531 | * \param data user argument, pointing to a drm_wait_vblank structure. |
541 | * \return zero on success or a negative number on failure. | 532 | * \return zero on success or a negative number on failure. |
542 | * | 533 | * |
543 | * Verifies the IRQ is installed. | 534 | * This function enables the vblank interrupt on the pipe requested, then |
544 | * | 535 | * sleeps waiting for the requested sequence number to occur, and drops |
545 | * 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 |
546 | * for the same vblank sequence number - nothing to be done in | 537 | * after a timeout with no further vblank waits scheduled). |
547 | * that case. If the number of tasks waiting for the interrupt exceeds 100 the | ||
548 | * function fails. Otherwise adds a new entry to drm_device::vbl_sigs for this | ||
549 | * task. | ||
550 | * | ||
551 | * If a signal is not requested, then calls vblank_wait(). | ||
552 | */ | 538 | */ |
553 | int drm_wait_vblank(struct drm_device *dev, void *data, | 539 | int drm_wait_vblank(struct drm_device *dev, void *data, |
554 | struct drm_file *file_priv) | 540 | struct drm_file *file_priv) |
@@ -560,6 +546,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
560 | if ((!dev->pdev->irq) || (!dev->irq_enabled)) | 546 | if ((!dev->pdev->irq) || (!dev->irq_enabled)) |
561 | return -EINVAL; | 547 | return -EINVAL; |
562 | 548 | ||
549 | if (vblwait->request.type & _DRM_VBLANK_SIGNAL) | ||
550 | return -EINVAL; | ||
551 | |||
563 | if (vblwait->request.type & | 552 | if (vblwait->request.type & |
564 | ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { | 553 | ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { |
565 | 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", |
@@ -597,89 +586,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
597 | vblwait->request.sequence = seq + 1; | 586 | vblwait->request.sequence = seq + 1; |
598 | } | 587 | } |
599 | 588 | ||
600 | if (flags & _DRM_VBLANK_SIGNAL) { | 589 | DRM_DEBUG("waiting on vblank count %d, crtc %d\n", |
601 | unsigned long irqflags; | 590 | vblwait->request.sequence, crtc); |
602 | struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; | 591 | dev->last_vblank_wait[crtc] = vblwait->request.sequence; |
603 | struct drm_vbl_sig *vbl_sig; | 592 | DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, |
604 | 593 | (((drm_vblank_count(dev, crtc) - | |
605 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 594 | vblwait->request.sequence) <= (1 << 23)) || |
606 | 595 | !dev->irq_enabled)); | |
607 | /* Check if this task has already scheduled the same signal | ||
608 | * for the same vblank sequence number; nothing to be done in | ||
609 | * that case | ||
610 | */ | ||
611 | list_for_each_entry(vbl_sig, vbl_sigs, head) { | ||
612 | if (vbl_sig->sequence == vblwait->request.sequence | ||
613 | && vbl_sig->info.si_signo == | ||
614 | vblwait->request.signal | ||
615 | && vbl_sig->task == current) { | ||
616 | spin_unlock_irqrestore(&dev->vbl_lock, | ||
617 | irqflags); | ||
618 | vblwait->reply.sequence = seq; | ||
619 | goto done; | ||
620 | } | ||
621 | } | ||
622 | |||
623 | if (atomic_read(&dev->vbl_signal_pending) >= 100) { | ||
624 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
625 | ret = -EBUSY; | ||
626 | goto done; | ||
627 | } | ||
628 | |||
629 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
630 | |||
631 | vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig), | ||
632 | DRM_MEM_DRIVER); | ||
633 | if (!vbl_sig) { | ||
634 | ret = -ENOMEM; | ||
635 | goto done; | ||
636 | } | ||
637 | |||
638 | /* Get a refcount on the vblank, which will be released by | ||
639 | * drm_vbl_send_signals(). | ||
640 | */ | ||
641 | ret = drm_vblank_get(dev, crtc); | ||
642 | if (ret) { | ||
643 | drm_free(vbl_sig, sizeof(struct drm_vbl_sig), | ||
644 | DRM_MEM_DRIVER); | ||
645 | goto done; | ||
646 | } | ||
647 | |||
648 | atomic_inc(&dev->vbl_signal_pending); | ||
649 | |||
650 | vbl_sig->sequence = vblwait->request.sequence; | ||
651 | vbl_sig->info.si_signo = vblwait->request.signal; | ||
652 | vbl_sig->task = current; | ||
653 | 596 | ||
654 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 597 | if (ret != -EINTR) { |
655 | 598 | struct timeval now; | |
656 | list_add_tail(&vbl_sig->head, vbl_sigs); | ||
657 | 599 | ||
658 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | 600 | do_gettimeofday(&now); |
659 | 601 | ||
660 | 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); | ||
661 | } else { | 607 | } else { |
662 | DRM_DEBUG("waiting on vblank count %d, crtc %d\n", | 608 | DRM_DEBUG("vblank wait interrupted by signal\n"); |
663 | vblwait->request.sequence, crtc); | ||
664 | dev->last_vblank_wait[crtc] = vblwait->request.sequence; | ||
665 | DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, | ||
666 | (((drm_vblank_count(dev, crtc) - | ||
667 | vblwait->request.sequence) <= (1 << 23)) || | ||
668 | !dev->irq_enabled)); | ||
669 | |||
670 | if (ret != -EINTR) { | ||
671 | struct timeval now; | ||
672 | |||
673 | do_gettimeofday(&now); | ||
674 | |||
675 | vblwait->reply.tval_sec = now.tv_sec; | ||
676 | vblwait->reply.tval_usec = now.tv_usec; | ||
677 | vblwait->reply.sequence = drm_vblank_count(dev, crtc); | ||
678 | DRM_DEBUG("returning %d to client\n", | ||
679 | vblwait->reply.sequence); | ||
680 | } else { | ||
681 | DRM_DEBUG("vblank wait interrupted by signal\n"); | ||
682 | } | ||
683 | } | 609 | } |
684 | 610 | ||
685 | done: | 611 | done: |
@@ -688,46 +614,6 @@ done: | |||
688 | } | 614 | } |
689 | 615 | ||
690 | /** | 616 | /** |
691 | * Send the VBLANK signals. | ||
692 | * | ||
693 | * \param dev DRM device. | ||
694 | * \param crtc CRTC where the vblank event occurred | ||
695 | * | ||
696 | * Sends a signal for each task in drm_device::vbl_sigs and empties the list. | ||
697 | * | ||
698 | * If a signal is not requested, then calls vblank_wait(). | ||
699 | */ | ||
700 | static void drm_vbl_send_signals(struct drm_device *dev, int crtc) | ||
701 | { | ||
702 | struct drm_vbl_sig *vbl_sig, *tmp; | ||
703 | struct list_head *vbl_sigs; | ||
704 | unsigned int vbl_seq; | ||
705 | unsigned long flags; | ||
706 | |||
707 | spin_lock_irqsave(&dev->vbl_lock, flags); | ||
708 | |||
709 | vbl_sigs = &dev->vbl_sigs[crtc]; | ||
710 | vbl_seq = drm_vblank_count(dev, crtc); | ||
711 | |||
712 | list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { | ||
713 | if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { | ||
714 | vbl_sig->info.si_code = vbl_seq; | ||
715 | send_sig_info(vbl_sig->info.si_signo, | ||
716 | &vbl_sig->info, vbl_sig->task); | ||
717 | |||
718 | list_del(&vbl_sig->head); | ||
719 | |||
720 | drm_free(vbl_sig, sizeof(*vbl_sig), | ||
721 | DRM_MEM_DRIVER); | ||
722 | atomic_dec(&dev->vbl_signal_pending); | ||
723 | drm_vblank_put(dev, crtc); | ||
724 | } | ||
725 | } | ||
726 | |||
727 | spin_unlock_irqrestore(&dev->vbl_lock, flags); | ||
728 | } | ||
729 | |||
730 | /** | ||
731 | * drm_handle_vblank - handle a vblank event | 617 | * drm_handle_vblank - handle a vblank event |
732 | * @dev: DRM device | 618 | * @dev: DRM device |
733 | * @crtc: where this event occurred | 619 | * @crtc: where this event occurred |
@@ -739,6 +625,5 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) | |||
739 | { | 625 | { |
740 | atomic_inc(&dev->_vblank_count[crtc]); | 626 | atomic_inc(&dev->_vblank_count[crtc]); |
741 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | 627 | DRM_WAKEUP(&dev->vbl_queue[crtc]); |
742 | drm_vbl_send_signals(dev, crtc); | ||
743 | } | 628 | } |
744 | EXPORT_SYMBOL(drm_handle_vblank); | 629 | EXPORT_SYMBOL(drm_handle_vblank); |