diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fops.c')
-rw-r--r-- | drivers/gpu/drm/drm_fops.c | 112 |
1 files changed, 110 insertions, 2 deletions
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 251bc0e3b5ec..08d14df3bb42 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); |
@@ -297,6 +300,18 @@ static int drm_open_helper(struct inode *inode, struct file *filp, | |||
297 | goto out_free; | 300 | goto out_free; |
298 | } | 301 | } |
299 | } | 302 | } |
303 | mutex_lock(&dev->struct_mutex); | ||
304 | if (dev->driver->master_set) { | ||
305 | ret = dev->driver->master_set(dev, priv, true); | ||
306 | if (ret) { | ||
307 | /* drop both references if this fails */ | ||
308 | drm_master_put(&priv->minor->master); | ||
309 | drm_master_put(&priv->master); | ||
310 | mutex_unlock(&dev->struct_mutex); | ||
311 | goto out_free; | ||
312 | } | ||
313 | } | ||
314 | mutex_unlock(&dev->struct_mutex); | ||
300 | } else { | 315 | } else { |
301 | /* get a reference to the master */ | 316 | /* get a reference to the master */ |
302 | priv->master = drm_master_get(priv->minor->master); | 317 | priv->master = drm_master_get(priv->minor->master); |
@@ -413,6 +428,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp) | |||
413 | } | 428 | } |
414 | } | 429 | } |
415 | 430 | ||
431 | static void drm_events_release(struct drm_file *file_priv) | ||
432 | { | ||
433 | struct drm_device *dev = file_priv->minor->dev; | ||
434 | struct drm_pending_event *e, *et; | ||
435 | struct drm_pending_vblank_event *v, *vt; | ||
436 | unsigned long flags; | ||
437 | |||
438 | spin_lock_irqsave(&dev->event_lock, flags); | ||
439 | |||
440 | /* Remove pending flips */ | ||
441 | list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link) | ||
442 | if (v->base.file_priv == file_priv) { | ||
443 | list_del(&v->base.link); | ||
444 | drm_vblank_put(dev, v->pipe); | ||
445 | v->base.destroy(&v->base); | ||
446 | } | ||
447 | |||
448 | /* Remove unconsumed events */ | ||
449 | list_for_each_entry_safe(e, et, &file_priv->event_list, link) | ||
450 | e->destroy(e); | ||
451 | |||
452 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
453 | } | ||
454 | |||
416 | /** | 455 | /** |
417 | * Release file. | 456 | * Release file. |
418 | * | 457 | * |
@@ -451,6 +490,8 @@ int drm_release(struct inode *inode, struct file *filp) | |||
451 | if (file_priv->minor->master) | 490 | if (file_priv->minor->master) |
452 | drm_master_release(dev, filp); | 491 | drm_master_release(dev, filp); |
453 | 492 | ||
493 | drm_events_release(file_priv); | ||
494 | |||
454 | if (dev->driver->driver_features & DRIVER_GEM) | 495 | if (dev->driver->driver_features & DRIVER_GEM) |
455 | drm_gem_release(dev, file_priv); | 496 | drm_gem_release(dev, file_priv); |
456 | 497 | ||
@@ -504,6 +545,8 @@ int drm_release(struct inode *inode, struct file *filp) | |||
504 | 545 | ||
505 | if (file_priv->minor->master == file_priv->master) { | 546 | if (file_priv->minor->master == file_priv->master) { |
506 | /* drop the reference held my the minor */ | 547 | /* drop the reference held my the minor */ |
548 | if (dev->driver->master_drop) | ||
549 | dev->driver->master_drop(dev, file_priv, true); | ||
507 | drm_master_put(&file_priv->minor->master); | 550 | drm_master_put(&file_priv->minor->master); |
508 | } | 551 | } |
509 | } | 552 | } |
@@ -544,9 +587,74 @@ int drm_release(struct inode *inode, struct file *filp) | |||
544 | } | 587 | } |
545 | EXPORT_SYMBOL(drm_release); | 588 | EXPORT_SYMBOL(drm_release); |
546 | 589 | ||
547 | /** No-op. */ | 590 | static bool |
591 | drm_dequeue_event(struct drm_file *file_priv, | ||
592 | size_t total, size_t max, struct drm_pending_event **out) | ||
593 | { | ||
594 | struct drm_device *dev = file_priv->minor->dev; | ||
595 | struct drm_pending_event *e; | ||
596 | unsigned long flags; | ||
597 | bool ret = false; | ||
598 | |||
599 | spin_lock_irqsave(&dev->event_lock, flags); | ||
600 | |||
601 | *out = NULL; | ||
602 | if (list_empty(&file_priv->event_list)) | ||
603 | goto out; | ||
604 | e = list_first_entry(&file_priv->event_list, | ||
605 | struct drm_pending_event, link); | ||
606 | if (e->event->length + total > max) | ||
607 | goto out; | ||
608 | |||
609 | file_priv->event_space += e->event->length; | ||
610 | list_del(&e->link); | ||
611 | *out = e; | ||
612 | ret = true; | ||
613 | |||
614 | out: | ||
615 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
616 | return ret; | ||
617 | } | ||
618 | |||
619 | ssize_t drm_read(struct file *filp, char __user *buffer, | ||
620 | size_t count, loff_t *offset) | ||
621 | { | ||
622 | struct drm_file *file_priv = filp->private_data; | ||
623 | struct drm_pending_event *e; | ||
624 | size_t total; | ||
625 | ssize_t ret; | ||
626 | |||
627 | ret = wait_event_interruptible(file_priv->event_wait, | ||
628 | !list_empty(&file_priv->event_list)); | ||
629 | if (ret < 0) | ||
630 | return ret; | ||
631 | |||
632 | total = 0; | ||
633 | while (drm_dequeue_event(file_priv, total, count, &e)) { | ||
634 | if (copy_to_user(buffer + total, | ||
635 | e->event, e->event->length)) { | ||
636 | total = -EFAULT; | ||
637 | break; | ||
638 | } | ||
639 | |||
640 | total += e->event->length; | ||
641 | e->destroy(e); | ||
642 | } | ||
643 | |||
644 | return total; | ||
645 | } | ||
646 | EXPORT_SYMBOL(drm_read); | ||
647 | |||
548 | unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) | 648 | unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) |
549 | { | 649 | { |
550 | return 0; | 650 | struct drm_file *file_priv = filp->private_data; |
651 | unsigned int mask = 0; | ||
652 | |||
653 | poll_wait(filp, &file_priv->event_wait, wait); | ||
654 | |||
655 | if (!list_empty(&file_priv->event_list)) | ||
656 | mask |= POLLIN | POLLRDNORM; | ||
657 | |||
658 | return mask; | ||
551 | } | 659 | } |
552 | EXPORT_SYMBOL(drm_poll); | 660 | EXPORT_SYMBOL(drm_poll); |