diff options
-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 | ||||
-rw-r--r-- | include/drm/drm.h | 33 | ||||
-rw-r--r-- | include/drm/drmP.h | 26 |
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 | ||
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 0a6f0b3bdc78..72754aca7abf 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -550,6 +550,62 @@ 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 | } | ||
589 | |||
590 | DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n", | ||
591 | vblwait->request.sequence, seq, pipe); | ||
592 | |||
593 | e->event.sequence = vblwait->request.sequence; | ||
594 | if ((seq - vblwait->request.sequence) <= (1 << 23)) { | ||
595 | e->event.tv_sec = now.tv_sec; | ||
596 | e->event.tv_usec = now.tv_usec; | ||
597 | drm_vblank_put(dev, e->pipe); | ||
598 | list_add_tail(&e->base.link, &e->base.file_priv->event_list); | ||
599 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
600 | } else { | ||
601 | list_add_tail(&e->base.link, &dev->vblank_event_list); | ||
602 | } | ||
603 | |||
604 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
605 | |||
606 | return 0; | ||
607 | } | ||
608 | |||
553 | /** | 609 | /** |
554 | * Wait for VBLANK. | 610 | * Wait for VBLANK. |
555 | * | 611 | * |
@@ -609,6 +665,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, | |||
609 | goto done; | 665 | goto done; |
610 | } | 666 | } |
611 | 667 | ||
668 | if (flags & _DRM_VBLANK_EVENT) | ||
669 | return drm_queue_vblank_event(dev, crtc, vblwait, file_priv); | ||
670 | |||
612 | if ((flags & _DRM_VBLANK_NEXTONMISS) && | 671 | if ((flags & _DRM_VBLANK_NEXTONMISS) && |
613 | (seq - vblwait->request.sequence) <= (1<<23)) { | 672 | (seq - vblwait->request.sequence) <= (1<<23)) { |
614 | vblwait->request.sequence = seq + 1; | 673 | vblwait->request.sequence = seq + 1; |
@@ -641,6 +700,38 @@ done: | |||
641 | return ret; | 700 | return ret; |
642 | } | 701 | } |
643 | 702 | ||
703 | void drm_handle_vblank_events(struct drm_device *dev, int crtc) | ||
704 | { | ||
705 | struct drm_pending_vblank_event *e, *t; | ||
706 | struct timeval now; | ||
707 | unsigned long flags; | ||
708 | unsigned int seq; | ||
709 | |||
710 | do_gettimeofday(&now); | ||
711 | seq = drm_vblank_count(dev, crtc); | ||
712 | |||
713 | spin_lock_irqsave(&dev->event_lock, flags); | ||
714 | |||
715 | list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { | ||
716 | if (e->pipe != crtc) | ||
717 | continue; | ||
718 | if ((seq - e->event.sequence) > (1<<23)) | ||
719 | continue; | ||
720 | |||
721 | DRM_DEBUG("vblank event on %d, current %d\n", | ||
722 | e->event.sequence, seq); | ||
723 | |||
724 | e->event.sequence = seq; | ||
725 | e->event.tv_sec = now.tv_sec; | ||
726 | e->event.tv_usec = now.tv_usec; | ||
727 | drm_vblank_put(dev, e->pipe); | ||
728 | list_move_tail(&e->base.link, &e->base.file_priv->event_list); | ||
729 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
730 | } | ||
731 | |||
732 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
733 | } | ||
734 | |||
644 | /** | 735 | /** |
645 | * drm_handle_vblank - handle a vblank event | 736 | * drm_handle_vblank - handle a vblank event |
646 | * @dev: DRM device | 737 | * @dev: DRM device |
@@ -651,7 +742,11 @@ done: | |||
651 | */ | 742 | */ |
652 | void drm_handle_vblank(struct drm_device *dev, int crtc) | 743 | void drm_handle_vblank(struct drm_device *dev, int crtc) |
653 | { | 744 | { |
745 | if (!dev->num_crtcs) | ||
746 | return; | ||
747 | |||
654 | atomic_inc(&dev->_vblank_count[crtc]); | 748 | atomic_inc(&dev->_vblank_count[crtc]); |
655 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | 749 | DRM_WAKEUP(&dev->vbl_queue[crtc]); |
750 | drm_handle_vblank_events(dev, crtc); | ||
656 | } | 751 | } |
657 | EXPORT_SYMBOL(drm_handle_vblank); | 752 | 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 7f436ec075f6..2fa217862058 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c | |||
@@ -333,6 +333,7 @@ static struct drm_driver driver = { | |||
333 | .mmap = drm_gem_mmap, | 333 | .mmap = drm_gem_mmap, |
334 | .poll = drm_poll, | 334 | .poll = drm_poll, |
335 | .fasync = drm_fasync, | 335 | .fasync = drm_fasync, |
336 | .read = drm_read, | ||
336 | #ifdef CONFIG_COMPAT | 337 | #ifdef CONFIG_COMPAT |
337 | .compat_ioctl = i915_compat_ioctl, | 338 | .compat_ioctl = i915_compat_ioctl, |
338 | #endif | 339 | #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 { | |||
454 | enum drm_vblank_seq_type { | 454 | enum 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 | ||
467 | struct drm_wait_vblank_request { | 468 | struct 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 | */ | ||
714 | struct drm_event { | ||
715 | __u32 type; | ||
716 | __u32 length; | ||
717 | }; | ||
718 | |||
719 | #define DRM_EVENT_VBLANK 0x01 | ||
720 | |||
721 | struct 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__ |
703 | typedef struct drm_clip_rect drm_clip_rect_t; | 732 | typedef struct drm_clip_rect drm_clip_rect_t; |
diff --git a/include/drm/drmP.h b/include/drm/drmP.h index c8e64bbadbcf..b0b36838ab11 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 */ | ||
430 | struct 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 */ |
430 | struct drm_file { | 438 | struct 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 */ |
@@ -900,6 +912,12 @@ struct drm_minor { | |||
900 | struct drm_mode_group mode_group; | 912 | struct drm_mode_group mode_group; |
901 | }; | 913 | }; |
902 | 914 | ||
915 | struct drm_pending_vblank_event { | ||
916 | struct drm_pending_event base; | ||
917 | int pipe; | ||
918 | struct drm_event_vblank event; | ||
919 | }; | ||
920 | |||
903 | /** | 921 | /** |
904 | * DRM device structure. This structure represent a complete card that | 922 | * DRM device structure. This structure represent a complete card that |
905 | * may contain multiple heads. | 923 | * may contain multiple heads. |
@@ -999,6 +1017,12 @@ struct drm_device { | |||
999 | 1017 | ||
1000 | u32 max_vblank_count; /**< size of vblank counter register */ | 1018 | u32 max_vblank_count; /**< size of vblank counter register */ |
1001 | 1019 | ||
1020 | /** | ||
1021 | * List of events | ||
1022 | */ | ||
1023 | struct list_head vblank_event_list; | ||
1024 | spinlock_t event_lock; | ||
1025 | |||
1002 | /*@} */ | 1026 | /*@} */ |
1003 | cycles_t ctx_start; | 1027 | cycles_t ctx_start; |
1004 | cycles_t lck_start; | 1028 | cycles_t lck_start; |
@@ -1135,6 +1159,8 @@ extern int drm_lastclose(struct drm_device *dev); | |||
1135 | extern int drm_open(struct inode *inode, struct file *filp); | 1159 | extern int drm_open(struct inode *inode, struct file *filp); |
1136 | extern int drm_stub_open(struct inode *inode, struct file *filp); | 1160 | extern int drm_stub_open(struct inode *inode, struct file *filp); |
1137 | extern int drm_fasync(int fd, struct file *filp, int on); | 1161 | extern int drm_fasync(int fd, struct file *filp, int on); |
1162 | extern ssize_t drm_read(struct file *filp, char __user *buffer, | ||
1163 | size_t count, loff_t *offset); | ||
1138 | extern int drm_release(struct inode *inode, struct file *filp); | 1164 | extern int drm_release(struct inode *inode, struct file *filp); |
1139 | 1165 | ||
1140 | /* Mapping support (drm_vm.h) */ | 1166 | /* Mapping support (drm_vm.h) */ |