mirror of
https://github.com/Stichting-MINIX-Research-Foundation/netbsd.git
synced 2025-08-10 14:39:47 -04:00
292 lines
8.2 KiB
C
292 lines
8.2 KiB
C
/* $NetBSD: drm_fops.c,v 1.4 2014/07/16 20:56:25 riastradh Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2013 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Taylor R. Campbell.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: drm_fops.c,v 1.4 2014/07/16 20:56:25 riastradh Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/select.h>
|
|
|
|
#include <drm/drmP.h>
|
|
|
|
static int drm_open_file_master(struct drm_file *);
|
|
|
|
static void drm_master_release(struct drm_file *);
|
|
static void drm_events_release(struct drm_file *);
|
|
static void drm_close_file_contexts(struct drm_file *);
|
|
static void drm_close_file_master(struct drm_file *);
|
|
|
|
int
|
|
drm_open_file(struct drm_file *file, void *fp, struct drm_minor *minor)
|
|
{
|
|
struct drm_device *const dev = minor->dev;
|
|
int ret;
|
|
|
|
file->always_authenticated = DRM_SUSER(); /* XXX */
|
|
file->always_authenticated = file->always_authenticated;
|
|
file->is_master = false;
|
|
file->stereo_allowed = false;
|
|
file->universal_planes = false;
|
|
file->magic = 0;
|
|
INIT_LIST_HEAD(&file->lhead);
|
|
file->minor = minor;
|
|
file->lock_count = 0;
|
|
/* file->object_idr is initialized by drm_gem_open. */
|
|
/* file->table_lock is initialized by drm_gem_open. */
|
|
file->filp = fp;
|
|
file->driver_priv = NULL;
|
|
file->master = NULL;
|
|
INIT_LIST_HEAD(&file->fbs);
|
|
linux_mutex_init(&file->fbs_lock);
|
|
DRM_INIT_WAITQUEUE(&file->event_wait, "drmevent");
|
|
selinit(&file->event_selq);
|
|
INIT_LIST_HEAD(&file->event_list);
|
|
file->event_space = 0x1000; /* XXX cargo-culted from Linux */
|
|
|
|
if (drm_core_check_feature(dev, DRIVER_GEM))
|
|
drm_gem_open(dev, file);
|
|
#ifndef __NetBSD__ /* XXX drm prime */
|
|
if (drm_core_check_feature(dev, DRIVER_PRIME))
|
|
drm_prime_init_file_private(&file->prime);
|
|
#endif
|
|
|
|
if (dev->driver->open) {
|
|
ret = (*dev->driver->open)(dev, file);
|
|
if (ret)
|
|
goto fail0;
|
|
}
|
|
|
|
ret = drm_open_file_master(file);
|
|
if (ret)
|
|
goto fail1;
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
list_add(&file->lhead, &dev->filelist);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
/* Success! */
|
|
return 0;
|
|
|
|
fail1: /*
|
|
* XXX This error branch needs scrutiny, but Linux's error
|
|
* branches are incomprehensible and look wronger.
|
|
*/
|
|
if (dev->driver->preclose)
|
|
(*dev->driver->preclose)(dev, file);
|
|
if (dev->driver->postclose)
|
|
(*dev->driver->postclose)(dev, file);
|
|
fail0:
|
|
#ifndef __NetBSD__ /* XXX drm prime */
|
|
if (drm_core_check_feature(dev, DRIVER_PRIME))
|
|
drm_prime_destroy_file_private(&file->prime);
|
|
#endif
|
|
if (drm_core_check_feature(dev, DRIVER_GEM))
|
|
drm_gem_release(dev, file);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
drm_open_file_master(struct drm_file *file)
|
|
{
|
|
struct drm_device *const dev = file->minor->dev;
|
|
int ret;
|
|
|
|
mutex_lock(&dev->master_mutex);
|
|
if (file->minor->master != NULL) {
|
|
file->master = drm_master_get(file->minor->master);
|
|
} else {
|
|
file->minor->master = drm_master_create(file->minor);
|
|
if (file->minor->master == NULL) {
|
|
ret = -ENOMEM;
|
|
goto fail0;
|
|
}
|
|
|
|
file->is_master = 1;
|
|
file->master = drm_master_get(file->minor->master);
|
|
file->authenticated = 1;
|
|
|
|
if (dev->driver->master_create) {
|
|
ret = (*dev->driver->master_create)(dev,
|
|
file->minor->master);
|
|
if (ret)
|
|
goto fail1;
|
|
}
|
|
|
|
if (dev->driver->master_set) {
|
|
ret = (*dev->driver->master_set)(dev, file, true);
|
|
if (ret)
|
|
goto fail1;
|
|
}
|
|
}
|
|
mutex_unlock(&dev->master_mutex);
|
|
|
|
/* Success! */
|
|
return 0;
|
|
|
|
fail1: mutex_unlock(&dev->master_mutex);
|
|
/* drm_master_put handles calling master_destroy for us. */
|
|
drm_master_put(&file->minor->master);
|
|
drm_master_put(&file->master);
|
|
fail0: KASSERT(ret);
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
drm_close_file(struct drm_file *file)
|
|
{
|
|
struct drm_minor *const minor = file->minor;
|
|
struct drm_device *const dev = minor->dev;
|
|
|
|
if (dev->driver->preclose)
|
|
(*dev->driver->preclose)(dev, file);
|
|
|
|
if (file->magic)
|
|
(void)drm_remove_magic(file->master, file->magic);
|
|
if (minor->master)
|
|
drm_master_release(file);
|
|
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
|
|
drm_core_reclaim_buffers(dev, file);
|
|
drm_events_release(file);
|
|
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
|
drm_fb_release(file);
|
|
if (drm_core_check_feature(dev, DRIVER_GEM))
|
|
drm_gem_release(dev, file);
|
|
drm_close_file_contexts(file);
|
|
drm_close_file_master(file);
|
|
|
|
mutex_lock(&dev->struct_mutex);
|
|
list_del(&file->lhead);
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
|
if (dev->driver->postclose)
|
|
(*dev->driver->postclose)(dev, file);
|
|
#ifndef __NetBSD__ /* XXX drm prime */
|
|
if (drm_core_check_feature(dev, DRIVER_PRIME))
|
|
drm_prime_destroy_file_private(&file->prime);
|
|
#endif
|
|
|
|
|
|
seldestroy(&file->event_selq);
|
|
DRM_DESTROY_WAITQUEUE(&file->event_wait);
|
|
linux_mutex_destroy(&file->fbs_lock);
|
|
}
|
|
|
|
static void
|
|
drm_master_release(struct drm_file *file)
|
|
{
|
|
|
|
/*
|
|
* XXX I think this locking concept is wrong -- we need to hold
|
|
* file->master->lock.spinlock across the two calls to
|
|
* drm_i_have_hw_lock and drm_lock_free.
|
|
*/
|
|
if (drm_i_have_hw_lock(file->minor->dev, file))
|
|
drm_lock_free(&file->master->lock,
|
|
_DRM_LOCKING_CONTEXT(file->master->lock.hw_lock->lock));
|
|
}
|
|
|
|
static void
|
|
drm_events_release(struct drm_file *file)
|
|
{
|
|
struct drm_device *const dev = file->minor->dev;
|
|
struct drm_pending_vblank_event *vblank, *vblank_next;
|
|
struct drm_pending_event *event, *event_next;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&dev->event_lock, flags);
|
|
|
|
list_for_each_entry_safe(vblank, vblank_next, &dev->vblank_event_list,
|
|
base.link) {
|
|
if (vblank->base.file_priv == file) {
|
|
list_del(&vblank->base.link);
|
|
drm_vblank_put(dev, vblank->pipe);
|
|
(*vblank->base.destroy)(&vblank->base);
|
|
}
|
|
}
|
|
list_for_each_entry_safe(event, event_next, &file->event_list, link) {
|
|
(*event->destroy)(event);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
}
|
|
|
|
static void
|
|
drm_close_file_contexts(struct drm_file *file)
|
|
{
|
|
struct drm_device *const dev = file->minor->dev;
|
|
struct drm_ctx_list *node, *next;
|
|
|
|
mutex_lock(&dev->ctxlist_mutex);
|
|
if (!list_empty(&dev->ctxlist)) {
|
|
list_for_each_entry_safe(node, next, &dev->ctxlist, head) {
|
|
if (node->tag != file)
|
|
continue;
|
|
if (node->handle == DRM_KERNEL_CONTEXT)
|
|
continue;
|
|
if (dev->driver->context_dtor)
|
|
(*dev->driver->context_dtor)(dev,
|
|
node->handle);
|
|
drm_ctxbitmap_free(dev, node->handle);
|
|
list_del(&node->head);
|
|
kfree(node);
|
|
}
|
|
}
|
|
mutex_unlock(&dev->ctxlist_mutex);
|
|
}
|
|
|
|
static void
|
|
drm_close_file_master(struct drm_file *file)
|
|
{
|
|
struct drm_device *const dev = file->minor->dev;
|
|
|
|
mutex_lock(&dev->master_mutex);
|
|
if (file->is_master) {
|
|
struct drm_file *other_file;
|
|
|
|
list_for_each_entry(other_file, &dev->filelist, lhead) {
|
|
if (other_file == file)
|
|
continue;
|
|
if (other_file->master != file->master)
|
|
continue;
|
|
other_file->authenticated = 0;
|
|
}
|
|
if (file->minor->master == file->master) {
|
|
if (dev->driver->master_drop)
|
|
(*dev->driver->master_drop)(dev, file, true);
|
|
drm_master_put(&file->minor->master);
|
|
}
|
|
}
|
|
if (file->master != NULL)
|
|
drm_master_put(&file->master);
|
|
file->is_master = 0;
|
|
mutex_unlock(&dev->master_mutex);
|
|
}
|