aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@bitplanet.net>2009-09-11 14:33:34 -0400
committerDave Airlie <airlied@redhat.com>2009-10-25 23:29:27 -0400
commitc182be37ed7cb04c344501b88b8fdb747016e6cf (patch)
tree013ae7e4c5bb512ac8e4132a642f40c27f03390e /drivers
parent0a5c1e61dbaceb6ce56281a3128a6912b0dcd043 (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.c98
-rw-r--r--drivers/gpu/drm/drm_irq.c95
-rw-r--r--drivers/gpu/drm/drm_stub.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c1
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
419static 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}
545EXPORT_SYMBOL(drm_release); 574EXPORT_SYMBOL(drm_release);
546 575
547/** No-op. */ 576static bool
577drm_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
600out:
601 spin_unlock_irqrestore(&dev->event_lock, flags);
602 return ret;
603}
604
605ssize_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}
632EXPORT_SYMBOL(drm_read);
633
548unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) 634unsigned 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}
552EXPORT_SYMBOL(drm_poll); 646EXPORT_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
526static 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
676void 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 */
625void drm_handle_vblank(struct drm_device *dev, int crtc) 716void 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}
630EXPORT_SYMBOL(drm_handle_vblank); 725EXPORT_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