aboutsummaryrefslogtreecommitdiffstats
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
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>
-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
-rw-r--r--include/drm/drm.h33
-rw-r--r--include/drm/drmP.h26
6 files changed, 251 insertions, 4 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
diff --git a/include/drm/drm.h b/include/drm/drm.h
index 7cb50bdde46d..fa6d9155873d 100644
--- a/include/drm/drm.h
+++ b/include/drm/drm.h
@@ -454,6 +454,7 @@ struct drm_irq_busid {
454enum drm_vblank_seq_type { 454enum drm_vblank_seq_type {
455 _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ 455 _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */
456 _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ 456 _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */
457 _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */
457 _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ 458 _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */
458 _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ 459 _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */
459 _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ 460 _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */
@@ -461,8 +462,8 @@ enum drm_vblank_seq_type {
461}; 462};
462 463
463#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) 464#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE)
464#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \ 465#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \
465 _DRM_VBLANK_NEXTONMISS) 466 _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)
466 467
467struct drm_wait_vblank_request { 468struct drm_wait_vblank_request {
468 enum drm_vblank_seq_type type; 469 enum drm_vblank_seq_type type;
@@ -698,6 +699,34 @@ struct drm_gem_open {
698#define DRM_COMMAND_BASE 0x40 699#define DRM_COMMAND_BASE 0x40
699#define DRM_COMMAND_END 0xA0 700#define DRM_COMMAND_END 0xA0
700 701
702/**
703 * Header for events written back to userspace on the drm fd. The
704 * type defines the type of event, the length specifies the total
705 * length of the event (including the header), and user_data is
706 * typically a 64 bit value passed with the ioctl that triggered the
707 * event. A read on the drm fd will always only return complete
708 * events, that is, if for example the read buffer is 100 bytes, and
709 * there are two 64 byte events pending, only one will be returned.
710 *
711 * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and
712 * up are chipset specific.
713 */
714struct drm_event {
715 __u32 type;
716 __u32 length;
717};
718
719#define DRM_EVENT_VBLANK 0x01
720
721struct drm_event_vblank {
722 struct drm_event base;
723 __u64 user_data;
724 __u32 tv_sec;
725 __u32 tv_usec;
726 __u32 sequence;
727 __u32 reserved;
728};
729
701/* typedef area */ 730/* typedef area */
702#ifndef __KERNEL__ 731#ifndef __KERNEL__
703typedef struct drm_clip_rect drm_clip_rect_t; 732typedef struct drm_clip_rect drm_clip_rect_t;
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index eeefb6369e19..fe52254df60c 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -426,6 +426,14 @@ struct drm_buf_entry {
426 struct drm_freelist freelist; 426 struct drm_freelist freelist;
427}; 427};
428 428
429/* Event queued up for userspace to read */
430struct drm_pending_event {
431 struct drm_event *event;
432 struct list_head link;
433 struct drm_file *file_priv;
434 void (*destroy)(struct drm_pending_event *event);
435};
436
429/** File private data */ 437/** File private data */
430struct drm_file { 438struct drm_file {
431 int authenticated; 439 int authenticated;
@@ -449,6 +457,10 @@ struct drm_file {
449 struct drm_master *master; /* master this node is currently associated with 457 struct drm_master *master; /* master this node is currently associated with
450 N.B. not always minor->master */ 458 N.B. not always minor->master */
451 struct list_head fbs; 459 struct list_head fbs;
460
461 wait_queue_head_t event_wait;
462 struct list_head event_list;
463 int event_space;
452}; 464};
453 465
454/** Wait queue */ 466/** Wait queue */
@@ -897,6 +909,12 @@ struct drm_minor {
897 struct drm_mode_group mode_group; 909 struct drm_mode_group mode_group;
898}; 910};
899 911
912struct drm_pending_vblank_event {
913 struct drm_pending_event base;
914 int pipe;
915 struct drm_event_vblank event;
916};
917
900/** 918/**
901 * DRM device structure. This structure represent a complete card that 919 * DRM device structure. This structure represent a complete card that
902 * may contain multiple heads. 920 * may contain multiple heads.
@@ -996,6 +1014,12 @@ struct drm_device {
996 1014
997 u32 max_vblank_count; /**< size of vblank counter register */ 1015 u32 max_vblank_count; /**< size of vblank counter register */
998 1016
1017 /**
1018 * List of events
1019 */
1020 struct list_head vblank_event_list;
1021 spinlock_t event_lock;
1022
999 /*@} */ 1023 /*@} */
1000 cycles_t ctx_start; 1024 cycles_t ctx_start;
1001 cycles_t lck_start; 1025 cycles_t lck_start;
@@ -1132,6 +1156,8 @@ extern int drm_lastclose(struct drm_device *dev);
1132extern int drm_open(struct inode *inode, struct file *filp); 1156extern int drm_open(struct inode *inode, struct file *filp);
1133extern int drm_stub_open(struct inode *inode, struct file *filp); 1157extern int drm_stub_open(struct inode *inode, struct file *filp);
1134extern int drm_fasync(int fd, struct file *filp, int on); 1158extern int drm_fasync(int fd, struct file *filp, int on);
1159extern ssize_t drm_read(struct file *filp, char __user *buffer,
1160 size_t count, loff_t *offset);
1135extern int drm_release(struct inode *inode, struct file *filp); 1161extern int drm_release(struct inode *inode, struct file *filp);
1136 1162
1137 /* Mapping support (drm_vm.h) */ 1163 /* Mapping support (drm_vm.h) */