diff options
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 130 |
1 files changed, 122 insertions, 8 deletions
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 0a6f0b3bdc78..7998ee66b317 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -429,15 +429,21 @@ int drm_vblank_get(struct drm_device *dev, int crtc) | |||
429 | 429 | ||
430 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 430 | spin_lock_irqsave(&dev->vbl_lock, irqflags); |
431 | /* Going from 0->1 means we have to enable interrupts again */ | 431 | /* Going from 0->1 means we have to enable interrupts again */ |
432 | if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1 && | 432 | if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) { |
433 | !dev->vblank_enabled[crtc]) { | 433 | if (!dev->vblank_enabled[crtc]) { |
434 | ret = dev->driver->enable_vblank(dev, crtc); | 434 | ret = dev->driver->enable_vblank(dev, crtc); |
435 | DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret); | 435 | DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret); |
436 | if (ret) | 436 | if (ret) |
437 | atomic_dec(&dev->vblank_refcount[crtc]); | ||
438 | else { | ||
439 | dev->vblank_enabled[crtc] = 1; | ||
440 | drm_update_vblank_count(dev, crtc); | ||
441 | } | ||
442 | } | ||
443 | } else { | ||
444 | if (!dev->vblank_enabled[crtc]) { | ||
437 | atomic_dec(&dev->vblank_refcount[crtc]); | 445 | atomic_dec(&dev->vblank_refcount[crtc]); |
438 | else { | 446 | ret = -EINVAL; |
439 | dev->vblank_enabled[crtc] = 1; | ||
440 | drm_update_vblank_count(dev, crtc); | ||
441 | } | 447 | } |
442 | } | 448 | } |
443 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | 449 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
@@ -464,6 +470,18 @@ void drm_vblank_put(struct drm_device *dev, int crtc) | |||
464 | } | 470 | } |
465 | EXPORT_SYMBOL(drm_vblank_put); | 471 | EXPORT_SYMBOL(drm_vblank_put); |
466 | 472 | ||
473 | void drm_vblank_off(struct drm_device *dev, int crtc) | ||
474 | { | ||
475 | unsigned long irqflags; | ||
476 | |||
477 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
478 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | ||
479 | dev->vblank_enabled[crtc] = 0; | ||
480 | dev->last_vblank[crtc] = dev->driver->get_vblank_counter(dev, crtc); | ||
481 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
482 | } | ||
483 | EXPORT_SYMBOL(drm_vblank_off); | ||
484 | |||
467 | /** | 485 | /** |
468 | * drm_vblank_pre_modeset - account for vblanks across mode sets | 486 | * drm_vblank_pre_modeset - account for vblanks across mode sets |
469 | * @dev: DRM device | 487 | * @dev: DRM device |
@@ -550,6 +568,63 @@ out: | |||
550 | return ret; | 568 | return ret; |
551 | } | 569 | } |
552 | 570 | ||
571 | static int drm_queue_vblank_event(struct drm_device *dev, int pipe, | ||
572 | union drm_wait_vblank *vblwait, | ||
573 | struct drm_file *file_priv) | ||
574 | { | ||
575 | struct drm_pending_vblank_event *e; | ||
576 | struct timeval now; | ||
577 | unsigned long flags; | ||
578 | unsigned int seq; | ||
579 | |||
580 | e = kzalloc(sizeof *e, GFP_KERNEL); | ||
581 | if (e == NULL) | ||
582 | return -ENOMEM; | ||
583 | |||
584 | e->pipe = pipe; | ||
585 | e->event.base.type = DRM_EVENT_VBLANK; | ||
586 | e->event.base.length = sizeof e->event; | ||
587 | e->event.user_data = vblwait->request.signal; | ||
588 | e->base.event = &e->event.base; | ||
589 | e->base.file_priv = file_priv; | ||
590 | e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; | ||
591 | |||
592 | do_gettimeofday(&now); | ||
593 | spin_lock_irqsave(&dev->event_lock, flags); | ||
594 | |||
595 | if (file_priv->event_space < sizeof e->event) { | ||
596 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
597 | kfree(e); | ||
598 | return -ENOMEM; | ||
599 | } | ||
600 | |||
601 | file_priv->event_space -= sizeof e->event; | ||
602 | seq = drm_vblank_count(dev, pipe); | ||
603 | if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) && | ||
604 | (seq - vblwait->request.sequence) <= (1 << 23)) { | ||
605 | vblwait->request.sequence = seq + 1; | ||
606 | vblwait->reply.sequence = vblwait->request.sequence; | ||
607 | } | ||
608 | |||
609 | DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n", | ||
610 | vblwait->request.sequence, seq, pipe); | ||
611 | |||
612 | e->event.sequence = vblwait->request.sequence; | ||
613 | if ((seq - vblwait->request.sequence) <= (1 << 23)) { | ||
614 | e->event.tv_sec = now.tv_sec; | ||
615 | e->event.tv_usec = now.tv_usec; | ||
616 | drm_vblank_put(dev, e->pipe); | ||
617 | list_add_tail(&e->base.link, &e->base.file_priv->event_list); | ||
618 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
619 | } else { | ||
620 | list_add_tail(&e->base.link, &dev->vblank_event_list); | ||
621 | } | ||
622 | |||
623 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | |||
553 | /** | 628 | /** |
554 | * Wait for VBLANK. | 629 | * Wait for VBLANK. |
555 | * | 630 | * |
@@ -609,6 +684,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
609 | goto done; | 684 | goto done; |
610 | } | 685 | } |
611 | 686 | ||
687 | if (flags & _DRM_VBLANK_EVENT) | ||
688 | return drm_queue_vblank_event(dev, crtc, vblwait, file_priv); | ||
689 | |||
612 | if ((flags & _DRM_VBLANK_NEXTONMISS) && | 690 | if ((flags & _DRM_VBLANK_NEXTONMISS) && |
613 | (seq - vblwait->request.sequence) <= (1<<23)) { | 691 | (seq - vblwait->request.sequence) <= (1<<23)) { |
614 | vblwait->request.sequence = seq + 1; | 692 | vblwait->request.sequence = seq + 1; |
@@ -641,6 +719,38 @@ done: | |||
641 | return ret; | 719 | return ret; |
642 | } | 720 | } |
643 | 721 | ||
722 | void drm_handle_vblank_events(struct drm_device *dev, int crtc) | ||
723 | { | ||
724 | struct drm_pending_vblank_event *e, *t; | ||
725 | struct timeval now; | ||
726 | unsigned long flags; | ||
727 | unsigned int seq; | ||
728 | |||
729 | do_gettimeofday(&now); | ||
730 | seq = drm_vblank_count(dev, crtc); | ||
731 | |||
732 | spin_lock_irqsave(&dev->event_lock, flags); | ||
733 | |||
734 | list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { | ||
735 | if (e->pipe != crtc) | ||
736 | continue; | ||
737 | if ((seq - e->event.sequence) > (1<<23)) | ||
738 | continue; | ||
739 | |||
740 | DRM_DEBUG("vblank event on %d, current %d\n", | ||
741 | e->event.sequence, seq); | ||
742 | |||
743 | e->event.sequence = seq; | ||
744 | e->event.tv_sec = now.tv_sec; | ||
745 | e->event.tv_usec = now.tv_usec; | ||
746 | drm_vblank_put(dev, e->pipe); | ||
747 | list_move_tail(&e->base.link, &e->base.file_priv->event_list); | ||
748 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
749 | } | ||
750 | |||
751 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
752 | } | ||
753 | |||
644 | /** | 754 | /** |
645 | * drm_handle_vblank - handle a vblank event | 755 | * drm_handle_vblank - handle a vblank event |
646 | * @dev: DRM device | 756 | * @dev: DRM device |
@@ -651,7 +761,11 @@ done: | |||
651 | */ | 761 | */ |
652 | void drm_handle_vblank(struct drm_device *dev, int crtc) | 762 | void drm_handle_vblank(struct drm_device *dev, int crtc) |
653 | { | 763 | { |
764 | if (!dev->num_crtcs) | ||
765 | return; | ||
766 | |||
654 | atomic_inc(&dev->_vblank_count[crtc]); | 767 | atomic_inc(&dev->_vblank_count[crtc]); |
655 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | 768 | DRM_WAKEUP(&dev->vbl_queue[crtc]); |
769 | drm_handle_vblank_events(dev, crtc); | ||
656 | } | 770 | } |
657 | EXPORT_SYMBOL(drm_handle_vblank); | 771 | EXPORT_SYMBOL(drm_handle_vblank); |