diff options
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 0a6f0b3bdc78..6b3ce6d38848 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -550,6 +550,63 @@ out: | |||
550 | return ret; | 550 | return ret; |
551 | } | 551 | } |
552 | 552 | ||
553 | static int drm_queue_vblank_event(struct drm_device *dev, int pipe, | ||
554 | union drm_wait_vblank *vblwait, | ||
555 | struct drm_file *file_priv) | ||
556 | { | ||
557 | struct drm_pending_vblank_event *e; | ||
558 | struct timeval now; | ||
559 | unsigned long flags; | ||
560 | unsigned int seq; | ||
561 | |||
562 | e = kzalloc(sizeof *e, GFP_KERNEL); | ||
563 | if (e == NULL) | ||
564 | return -ENOMEM; | ||
565 | |||
566 | e->pipe = pipe; | ||
567 | e->event.base.type = DRM_EVENT_VBLANK; | ||
568 | e->event.base.length = sizeof e->event; | ||
569 | e->event.user_data = vblwait->request.signal; | ||
570 | e->base.event = &e->event.base; | ||
571 | e->base.file_priv = file_priv; | ||
572 | e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; | ||
573 | |||
574 | do_gettimeofday(&now); | ||
575 | spin_lock_irqsave(&dev->event_lock, flags); | ||
576 | |||
577 | if (file_priv->event_space < sizeof e->event) { | ||
578 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
579 | kfree(e); | ||
580 | return -ENOMEM; | ||
581 | } | ||
582 | |||
583 | file_priv->event_space -= sizeof e->event; | ||
584 | seq = drm_vblank_count(dev, pipe); | ||
585 | if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) && | ||
586 | (seq - vblwait->request.sequence) <= (1 << 23)) { | ||
587 | vblwait->request.sequence = seq + 1; | ||
588 | vblwait->reply.sequence = vblwait->request.sequence; | ||
589 | } | ||
590 | |||
591 | DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n", | ||
592 | vblwait->request.sequence, seq, pipe); | ||
593 | |||
594 | e->event.sequence = vblwait->request.sequence; | ||
595 | if ((seq - vblwait->request.sequence) <= (1 << 23)) { | ||
596 | e->event.tv_sec = now.tv_sec; | ||
597 | e->event.tv_usec = now.tv_usec; | ||
598 | drm_vblank_put(dev, e->pipe); | ||
599 | list_add_tail(&e->base.link, &e->base.file_priv->event_list); | ||
600 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
601 | } else { | ||
602 | list_add_tail(&e->base.link, &dev->vblank_event_list); | ||
603 | } | ||
604 | |||
605 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
606 | |||
607 | return 0; | ||
608 | } | ||
609 | |||
553 | /** | 610 | /** |
554 | * Wait for VBLANK. | 611 | * Wait for VBLANK. |
555 | * | 612 | * |
@@ -609,6 +666,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
609 | goto done; | 666 | goto done; |
610 | } | 667 | } |
611 | 668 | ||
669 | if (flags & _DRM_VBLANK_EVENT) | ||
670 | return drm_queue_vblank_event(dev, crtc, vblwait, file_priv); | ||
671 | |||
612 | if ((flags & _DRM_VBLANK_NEXTONMISS) && | 672 | if ((flags & _DRM_VBLANK_NEXTONMISS) && |
613 | (seq - vblwait->request.sequence) <= (1<<23)) { | 673 | (seq - vblwait->request.sequence) <= (1<<23)) { |
614 | vblwait->request.sequence = seq + 1; | 674 | vblwait->request.sequence = seq + 1; |
@@ -641,6 +701,38 @@ done: | |||
641 | return ret; | 701 | return ret; |
642 | } | 702 | } |
643 | 703 | ||
704 | void drm_handle_vblank_events(struct drm_device *dev, int crtc) | ||
705 | { | ||
706 | struct drm_pending_vblank_event *e, *t; | ||
707 | struct timeval now; | ||
708 | unsigned long flags; | ||
709 | unsigned int seq; | ||
710 | |||
711 | do_gettimeofday(&now); | ||
712 | seq = drm_vblank_count(dev, crtc); | ||
713 | |||
714 | spin_lock_irqsave(&dev->event_lock, flags); | ||
715 | |||
716 | list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { | ||
717 | if (e->pipe != crtc) | ||
718 | continue; | ||
719 | if ((seq - e->event.sequence) > (1<<23)) | ||
720 | continue; | ||
721 | |||
722 | DRM_DEBUG("vblank event on %d, current %d\n", | ||
723 | e->event.sequence, seq); | ||
724 | |||
725 | e->event.sequence = seq; | ||
726 | e->event.tv_sec = now.tv_sec; | ||
727 | e->event.tv_usec = now.tv_usec; | ||
728 | drm_vblank_put(dev, e->pipe); | ||
729 | list_move_tail(&e->base.link, &e->base.file_priv->event_list); | ||
730 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
731 | } | ||
732 | |||
733 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
734 | } | ||
735 | |||
644 | /** | 736 | /** |
645 | * drm_handle_vblank - handle a vblank event | 737 | * drm_handle_vblank - handle a vblank event |
646 | * @dev: DRM device | 738 | * @dev: DRM device |
@@ -651,7 +743,11 @@ done: | |||
651 | */ | 743 | */ |
652 | void drm_handle_vblank(struct drm_device *dev, int crtc) | 744 | void drm_handle_vblank(struct drm_device *dev, int crtc) |
653 | { | 745 | { |
746 | if (!dev->num_crtcs) | ||
747 | return; | ||
748 | |||
654 | atomic_inc(&dev->_vblank_count[crtc]); | 749 | atomic_inc(&dev->_vblank_count[crtc]); |
655 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | 750 | DRM_WAKEUP(&dev->vbl_queue[crtc]); |
751 | drm_handle_vblank_events(dev, crtc); | ||
656 | } | 752 | } |
657 | EXPORT_SYMBOL(drm_handle_vblank); | 753 | EXPORT_SYMBOL(drm_handle_vblank); |