diff options
author | Kristian Høgsberg <krh@bitplanet.net> | 2009-09-11 14:33:34 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-10-25 23:29:27 -0400 |
commit | c182be37ed7cb04c344501b88b8fdb747016e6cf (patch) | |
tree | 013ae7e4c5bb512ac8e4132a642f40c27f03390e /drivers | |
parent | 0a5c1e61dbaceb6ce56281a3128a6912b0dcd043 (diff) |
drm: Add async event synchronization for drmWaitVblank
This patch adds a new flag to the drmWaitVblank ioctl, which asks the drm
to return immediately and notify userspace when the specified vblank sequence
happens by sending an event back on the drm fd.
The event mechanism works with the other flags supported by the ioctls,
specifically, the vblank sequence can be specified relatively or absolutely,
and works for primary and seconday crtc.
The signal field of the vblank request is used to provide user data,
which will be sent back to user space in the vblank event.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/drm_fops.c | 98 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 95 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_stub.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 1 |
4 files changed, 194 insertions, 2 deletions
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 251bc0e3b5ec..8ac7fbf6b2b7 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c | |||
@@ -257,6 +257,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp, | |||
257 | 257 | ||
258 | INIT_LIST_HEAD(&priv->lhead); | 258 | INIT_LIST_HEAD(&priv->lhead); |
259 | INIT_LIST_HEAD(&priv->fbs); | 259 | INIT_LIST_HEAD(&priv->fbs); |
260 | INIT_LIST_HEAD(&priv->event_list); | ||
261 | init_waitqueue_head(&priv->event_wait); | ||
262 | priv->event_space = 4096; /* set aside 4k for event buffer */ | ||
260 | 263 | ||
261 | if (dev->driver->driver_features & DRIVER_GEM) | 264 | if (dev->driver->driver_features & DRIVER_GEM) |
262 | drm_gem_open(dev, priv); | 265 | drm_gem_open(dev, priv); |
@@ -413,6 +416,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp) | |||
413 | } | 416 | } |
414 | } | 417 | } |
415 | 418 | ||
419 | static void drm_events_release(struct drm_file *file_priv) | ||
420 | { | ||
421 | struct drm_device *dev = file_priv->minor->dev; | ||
422 | struct drm_pending_event *e, *et; | ||
423 | struct drm_pending_vblank_event *v, *vt; | ||
424 | unsigned long flags; | ||
425 | |||
426 | spin_lock_irqsave(&dev->event_lock, flags); | ||
427 | |||
428 | /* Remove pending flips */ | ||
429 | list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link) | ||
430 | if (v->base.file_priv == file_priv) { | ||
431 | list_del(&v->base.link); | ||
432 | drm_vblank_put(dev, v->pipe); | ||
433 | v->base.destroy(&v->base); | ||
434 | } | ||
435 | |||
436 | /* Remove unconsumed events */ | ||
437 | list_for_each_entry_safe(e, et, &file_priv->event_list, link) | ||
438 | e->destroy(e); | ||
439 | |||
440 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
441 | } | ||
442 | |||
416 | /** | 443 | /** |
417 | * Release file. | 444 | * Release file. |
418 | * | 445 | * |
@@ -451,6 +478,8 @@ int drm_release(struct inode *inode, struct file *filp) | |||
451 | if (file_priv->minor->master) | 478 | if (file_priv->minor->master) |
452 | drm_master_release(dev, filp); | 479 | drm_master_release(dev, filp); |
453 | 480 | ||
481 | drm_events_release(file_priv); | ||
482 | |||
454 | if (dev->driver->driver_features & DRIVER_GEM) | 483 | if (dev->driver->driver_features & DRIVER_GEM) |
455 | drm_gem_release(dev, file_priv); | 484 | drm_gem_release(dev, file_priv); |
456 | 485 | ||
@@ -544,9 +573,74 @@ int drm_release(struct inode *inode, struct file *filp) | |||
544 | } | 573 | } |
545 | EXPORT_SYMBOL(drm_release); | 574 | EXPORT_SYMBOL(drm_release); |
546 | 575 | ||
547 | /** No-op. */ | 576 | static bool |
577 | drm_dequeue_event(struct drm_file *file_priv, | ||
578 | size_t total, size_t max, struct drm_pending_event **out) | ||
579 | { | ||
580 | struct drm_device *dev = file_priv->minor->dev; | ||
581 | struct drm_pending_event *e; | ||
582 | unsigned long flags; | ||
583 | bool ret = false; | ||
584 | |||
585 | spin_lock_irqsave(&dev->event_lock, flags); | ||
586 | |||
587 | *out = NULL; | ||
588 | if (list_empty(&file_priv->event_list)) | ||
589 | goto out; | ||
590 | e = list_first_entry(&file_priv->event_list, | ||
591 | struct drm_pending_event, link); | ||
592 | if (e->event->length + total > max) | ||
593 | goto out; | ||
594 | |||
595 | file_priv->event_space += e->event->length; | ||
596 | list_del(&e->link); | ||
597 | *out = e; | ||
598 | ret = true; | ||
599 | |||
600 | out: | ||
601 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
602 | return ret; | ||
603 | } | ||
604 | |||
605 | ssize_t drm_read(struct file *filp, char __user *buffer, | ||
606 | size_t count, loff_t *offset) | ||
607 | { | ||
608 | struct drm_file *file_priv = filp->private_data; | ||
609 | struct drm_pending_event *e; | ||
610 | size_t total; | ||
611 | ssize_t ret; | ||
612 | |||
613 | ret = wait_event_interruptible(file_priv->event_wait, | ||
614 | !list_empty(&file_priv->event_list)); | ||
615 | if (ret < 0) | ||
616 | return ret; | ||
617 | |||
618 | total = 0; | ||
619 | while (drm_dequeue_event(file_priv, total, count, &e)) { | ||
620 | if (copy_to_user(buffer + total, | ||
621 | e->event, e->event->length)) { | ||
622 | total = -EFAULT; | ||
623 | break; | ||
624 | } | ||
625 | |||
626 | total += e->event->length; | ||
627 | e->destroy(e); | ||
628 | } | ||
629 | |||
630 | return total; | ||
631 | } | ||
632 | EXPORT_SYMBOL(drm_read); | ||
633 | |||
548 | unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) | 634 | unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) |
549 | { | 635 | { |
550 | return 0; | 636 | struct drm_file *file_priv = filp->private_data; |
637 | unsigned int mask = 0; | ||
638 | |||
639 | poll_wait(filp, &file_priv->event_wait, wait); | ||
640 | |||
641 | if (!list_empty(&file_priv->event_list)) | ||
642 | mask |= POLLIN | POLLRDNORM; | ||
643 | |||
644 | return mask; | ||
551 | } | 645 | } |
552 | EXPORT_SYMBOL(drm_poll); | 646 | EXPORT_SYMBOL(drm_poll); |
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index f85aaf21e783..d9af7964f81c 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -523,6 +523,62 @@ out: | |||
523 | return ret; | 523 | return ret; |
524 | } | 524 | } |
525 | 525 | ||
526 | static int drm_queue_vblank_event(struct drm_device *dev, int pipe, | ||
527 | union drm_wait_vblank *vblwait, | ||
528 | struct drm_file *file_priv) | ||
529 | { | ||
530 | struct drm_pending_vblank_event *e; | ||
531 | struct timeval now; | ||
532 | unsigned long flags; | ||
533 | unsigned int seq; | ||
534 | |||
535 | e = kzalloc(sizeof *e, GFP_KERNEL); | ||
536 | if (e == NULL) | ||
537 | return -ENOMEM; | ||
538 | |||
539 | e->pipe = pipe; | ||
540 | e->event.base.type = DRM_EVENT_VBLANK; | ||
541 | e->event.base.length = sizeof e->event; | ||
542 | e->event.user_data = vblwait->request.signal; | ||
543 | e->base.event = &e->event.base; | ||
544 | e->base.file_priv = file_priv; | ||
545 | e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; | ||
546 | |||
547 | do_gettimeofday(&now); | ||
548 | spin_lock_irqsave(&dev->event_lock, flags); | ||
549 | |||
550 | if (file_priv->event_space < sizeof e->event) { | ||
551 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
552 | kfree(e); | ||
553 | return -ENOMEM; | ||
554 | } | ||
555 | |||
556 | file_priv->event_space -= sizeof e->event; | ||
557 | seq = drm_vblank_count(dev, pipe); | ||
558 | if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) && | ||
559 | (seq - vblwait->request.sequence) <= (1 << 23)) { | ||
560 | vblwait->request.sequence = seq + 1; | ||
561 | } | ||
562 | |||
563 | DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n", | ||
564 | vblwait->request.sequence, seq, pipe); | ||
565 | |||
566 | e->event.sequence = vblwait->request.sequence; | ||
567 | if ((seq - vblwait->request.sequence) <= (1 << 23)) { | ||
568 | e->event.tv_sec = now.tv_sec; | ||
569 | e->event.tv_usec = now.tv_usec; | ||
570 | drm_vblank_put(dev, e->pipe); | ||
571 | list_add_tail(&e->base.link, &e->base.file_priv->event_list); | ||
572 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
573 | } else { | ||
574 | list_add_tail(&e->base.link, &dev->vblank_event_list); | ||
575 | } | ||
576 | |||
577 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
526 | /** | 582 | /** |
527 | * Wait for VBLANK. | 583 | * Wait for VBLANK. |
528 | * | 584 | * |
@@ -582,6 +638,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
582 | goto done; | 638 | goto done; |
583 | } | 639 | } |
584 | 640 | ||
641 | if (flags & _DRM_VBLANK_EVENT) | ||
642 | return drm_queue_vblank_event(dev, crtc, vblwait, file_priv); | ||
643 | |||
585 | if ((flags & _DRM_VBLANK_NEXTONMISS) && | 644 | if ((flags & _DRM_VBLANK_NEXTONMISS) && |
586 | (seq - vblwait->request.sequence) <= (1<<23)) { | 645 | (seq - vblwait->request.sequence) <= (1<<23)) { |
587 | vblwait->request.sequence = seq + 1; | 646 | vblwait->request.sequence = seq + 1; |
@@ -614,6 +673,38 @@ done: | |||
614 | return ret; | 673 | return ret; |
615 | } | 674 | } |
616 | 675 | ||
676 | void drm_handle_vblank_events(struct drm_device *dev, int crtc) | ||
677 | { | ||
678 | struct drm_pending_vblank_event *e, *t; | ||
679 | struct timeval now; | ||
680 | unsigned long flags; | ||
681 | unsigned int seq; | ||
682 | |||
683 | do_gettimeofday(&now); | ||
684 | seq = drm_vblank_count(dev, crtc); | ||
685 | |||
686 | spin_lock_irqsave(&dev->event_lock, flags); | ||
687 | |||
688 | list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { | ||
689 | if (e->pipe != crtc) | ||
690 | continue; | ||
691 | if ((seq - e->event.sequence) > (1<<23)) | ||
692 | continue; | ||
693 | |||
694 | DRM_DEBUG("vblank event on %d, current %d\n", | ||
695 | e->event.sequence, seq); | ||
696 | |||
697 | e->event.sequence = seq; | ||
698 | e->event.tv_sec = now.tv_sec; | ||
699 | e->event.tv_usec = now.tv_usec; | ||
700 | drm_vblank_put(dev, e->pipe); | ||
701 | list_move_tail(&e->base.link, &e->base.file_priv->event_list); | ||
702 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
703 | } | ||
704 | |||
705 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
706 | } | ||
707 | |||
617 | /** | 708 | /** |
618 | * drm_handle_vblank - handle a vblank event | 709 | * drm_handle_vblank - handle a vblank event |
619 | * @dev: DRM device | 710 | * @dev: DRM device |
@@ -624,7 +715,11 @@ done: | |||
624 | */ | 715 | */ |
625 | void drm_handle_vblank(struct drm_device *dev, int crtc) | 716 | void drm_handle_vblank(struct drm_device *dev, int crtc) |
626 | { | 717 | { |
718 | if (!dev->num_crtcs) | ||
719 | return; | ||
720 | |||
627 | atomic_inc(&dev->_vblank_count[crtc]); | 721 | atomic_inc(&dev->_vblank_count[crtc]); |
628 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | 722 | DRM_WAKEUP(&dev->vbl_queue[crtc]); |
723 | drm_handle_vblank_events(dev, crtc); | ||
629 | } | 724 | } |
630 | EXPORT_SYMBOL(drm_handle_vblank); | 725 | EXPORT_SYMBOL(drm_handle_vblank); |
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 55bb8a82d612..adb864dfef3e 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c | |||
@@ -220,9 +220,11 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, | |||
220 | INIT_LIST_HEAD(&dev->ctxlist); | 220 | INIT_LIST_HEAD(&dev->ctxlist); |
221 | INIT_LIST_HEAD(&dev->vmalist); | 221 | INIT_LIST_HEAD(&dev->vmalist); |
222 | INIT_LIST_HEAD(&dev->maplist); | 222 | INIT_LIST_HEAD(&dev->maplist); |
223 | INIT_LIST_HEAD(&dev->vblank_event_list); | ||
223 | 224 | ||
224 | spin_lock_init(&dev->count_lock); | 225 | spin_lock_init(&dev->count_lock); |
225 | spin_lock_init(&dev->drw_lock); | 226 | spin_lock_init(&dev->drw_lock); |
227 | spin_lock_init(&dev->event_lock); | ||
226 | init_timer(&dev->timer); | 228 | init_timer(&dev->timer); |
227 | mutex_init(&dev->struct_mutex); | 229 | mutex_init(&dev->struct_mutex); |
228 | mutex_init(&dev->ctxlist_mutex); | 230 | mutex_init(&dev->ctxlist_mutex); |
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index dbe568c9327b..b81305e33c79 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c | |||
@@ -206,6 +206,7 @@ static struct drm_driver driver = { | |||
206 | .mmap = drm_gem_mmap, | 206 | .mmap = drm_gem_mmap, |
207 | .poll = drm_poll, | 207 | .poll = drm_poll, |
208 | .fasync = drm_fasync, | 208 | .fasync = drm_fasync, |
209 | .read = drm_read, | ||
209 | #ifdef CONFIG_COMPAT | 210 | #ifdef CONFIG_COMPAT |
210 | .compat_ioctl = i915_compat_ioctl, | 211 | .compat_ioctl = i915_compat_ioctl, |
211 | #endif | 212 | #endif |