diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fops.c')
-rw-r--r-- | drivers/gpu/drm/drm_fops.c | 98 |
1 files changed, 96 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); |