root/sys/dev/drm/drm_drv.c
/*
 * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
 *
 * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
 * All Rights Reserved.
 *
 * Author Rickard E. (Rik) Faith <faith@valinux.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mount.h>
#include <linux/slab.h>
#include <linux/srcu.h>

#include <drm/drm_client.h>
#include <drm/drm_drv.h>
#include <drm/drmP.h>

#include "drm_crtc_internal.h"
#include "drm_legacy.h"
#include "drm_internal.h"
#if 0 /* included twice */
#include "drm_crtc_internal.h"
#endif

/*
 * drm_debug: Enable debug output.
 * Bitmask of DRM_UT_x. See include/drm/drmP.h for details.
 */
#ifdef __DragonFly__
/* Provides three levels of debug: off, minimal, verbose */
#if DRM_DEBUG_DEFAULT_ON == 1
#define DRM_DEBUGBITS_ON (DRM_UT_CORE | DRM_UT_DRIVER | DRM_UT_KMS |    \
                          DRM_UT_PRIME| DRM_UT_ATOMIC | DRM_UT_FIOCTL)
#elif DRM_DEBUG_DEFAULT_ON == 2
#define DRM_DEBUGBITS_ON (DRM_UT_CORE | DRM_UT_DRIVER | DRM_UT_KMS |    \
                          DRM_UT_PRIME| DRM_UT_ATOMIC | DRM_UT_FIOCTL | \
                          DRM_UT_PID  | DRM_UT_IOCTL  )
#else
#define DRM_DEBUGBITS_ON (0x0)
#endif

unsigned int drm_debug = DRM_DEBUGBITS_ON;      /* defaults to 0 */
#else
unsigned int drm_debug = 0;
#endif /* __DragonFly__ */
EXPORT_SYMBOL(drm_debug);

MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl");
MODULE_DESCRIPTION("DRM shared core routines");
MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug category.\n"
"\t\tBit 0 (0x01)  will enable CORE messages (drm core code)\n"
"\t\tBit 1 (0x02)  will enable DRIVER messages (drm controller code)\n"
"\t\tBit 2 (0x04)  will enable KMS messages (modesetting code)\n"
"\t\tBit 3 (0x08)  will enable PRIME messages (prime code)\n"
"\t\tBit 4 (0x10)  will enable ATOMIC messages (atomic code)\n"
"\t\tBit 5 (0x20)  will enable VBL messages (vblank code)\n"
"\t\tBit 7 (0x80)  will enable LEASE messages (leasing code)\n"
"\t\tBit 8 (0x100) will enable DP messages (displayport code)");
module_param_named(debug, drm_debug, int, 0600);

static DEFINE_MUTEX(drm_minor_lock);
static struct idr drm_minors_idr;

/*
 * If the drm core fails to init for whatever reason,
 * we should prevent any drivers from registering with it.
 * It's best to check this at drm_dev_init(), as some drivers
 * prefer to embed struct drm_device into their own device
 * structure and call drm_dev_init() themselves.
 */
static bool drm_core_init_complete = false;

#if 0
static struct dentry *drm_debugfs_root;
#endif

#if 0
DEFINE_STATIC_SRCU(drm_unplug_srcu);
#endif

/*
 * DRM Minors
 * A DRM device can provide several char-dev interfaces on the DRM-Major. Each
 * of them is represented by a drm_minor object. Depending on the capabilities
 * of the device-driver, different interfaces are registered.
 *
 * Minors can be accessed via dev->$minor_name. This pointer is either
 * NULL or a valid drm_minor pointer and stays valid as long as the device is
 * valid. This means, DRM minors have the same life-time as the underlying
 * device. However, this doesn't mean that the minor is active. Minors are
 * registered and unregistered dynamically according to device-state.
 */

static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
                                             unsigned int type)
{
        switch (type) {
        case DRM_MINOR_PRIMARY:
                return &dev->primary;
        case DRM_MINOR_RENDER:
                return &dev->render;
        default:
                BUG();
        }
}

static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
{
        struct drm_minor *minor;
        unsigned long flags;
        int r;

        minor = kzalloc(sizeof(*minor), GFP_KERNEL);
        if (!minor)
                return -ENOMEM;

        minor->type = type;
        minor->dev = dev;

        idr_preload(GFP_KERNEL);
        spin_lock_irqsave(&drm_minor_lock, flags);
        r = idr_alloc(&drm_minors_idr,
                      NULL,
                      64 * type,
                      64 * (type + 1),
                      GFP_NOWAIT);
        spin_unlock_irqrestore(&drm_minor_lock, flags);
        idr_preload_end();

        if (r < 0)
                goto err_free;

        minor->index = r;

        minor->kdev = drm_sysfs_minor_alloc(minor);
        if (IS_ERR(minor->kdev)) {
                r = PTR_ERR(minor->kdev);
                goto err_index;
        }

        *drm_minor_get_slot(dev, type) = minor;
        return 0;

err_index:
        spin_lock_irqsave(&drm_minor_lock, flags);
        idr_remove(&drm_minors_idr, minor->index);
        spin_unlock_irqrestore(&drm_minor_lock, flags);
err_free:
        kfree(minor);
        return r;
}

static void drm_minor_free(struct drm_device *dev, unsigned int type)
{
        struct drm_minor **slot, *minor;
        unsigned long flags;

        slot = drm_minor_get_slot(dev, type);
        minor = *slot;
        if (!minor)
                return;

#if 0
        put_device(minor->kdev);
#endif

        spin_lock_irqsave(&drm_minor_lock, flags);
        idr_remove(&drm_minors_idr, minor->index);
        spin_unlock_irqrestore(&drm_minor_lock, flags);

        kfree(minor);
        *slot = NULL;
}

static int drm_minor_register(struct drm_device *dev, unsigned int type)
{
        struct drm_minor *minor;
        unsigned long flags;
        int ret;

        DRM_DEBUG("\n");

        minor = *drm_minor_get_slot(dev, type);
        if (!minor)
                return 0;

#if 0
        ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root);
        if (ret) {
                DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
                goto err_debugfs;
        }

        ret = device_add(minor->kdev);
        if (ret)
                goto err_debugfs;
#endif

        /* replace NULL with @minor so lookups will succeed from now on */
        spin_lock_irqsave(&drm_minor_lock, flags);
        idr_replace(&drm_minors_idr, minor, minor->index);
        spin_unlock_irqrestore(&drm_minor_lock, flags);

        DRM_DEBUG("new minor registered %d\n", minor->index);
        return 0;

#if 0
err_debugfs:
        drm_debugfs_cleanup(minor);
#endif
        return ret;
}

static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
{
        struct drm_minor *minor;
        unsigned long flags;

        minor = *drm_minor_get_slot(dev, type);
#if 0
        if (!minor || !device_is_registered(minor->kdev))
#else
        if (!minor)
#endif
                return;

        /* replace @minor with NULL so lookups will fail from now on */
        spin_lock_irqsave(&drm_minor_lock, flags);
        idr_replace(&drm_minors_idr, NULL, minor->index);
        spin_unlock_irqrestore(&drm_minor_lock, flags);

#if 0
        device_del(minor->kdev);
#endif
        dev_set_drvdata(minor->kdev, NULL); /* safety belt */
        drm_debugfs_cleanup(minor);
}

/*
 * Looks up the given minor-ID and returns the respective DRM-minor object. The
 * refence-count of the underlying device is increased so you must release this
 * object with drm_minor_release().
 *
 * As long as you hold this minor, it is guaranteed that the object and the
 * minor->dev pointer will stay valid! However, the device may get unplugged and
 * unregistered while you hold the minor.
 */
struct drm_minor *drm_minor_acquire(unsigned int minor_id)
{
        struct drm_minor *minor;
        unsigned long flags;

        spin_lock_irqsave(&drm_minor_lock, flags);
        minor = idr_find(&drm_minors_idr, minor_id);
        if (minor)
                drm_dev_get(minor->dev);
        spin_unlock_irqrestore(&drm_minor_lock, flags);

        if (!minor) {
                return ERR_PTR(-ENODEV);
        } else if (drm_dev_is_unplugged(minor->dev)) {
                drm_dev_put(minor->dev);
                return ERR_PTR(-ENODEV);
        }

        return minor;
}

void drm_minor_release(struct drm_minor *minor)
{
        drm_dev_put(minor->dev);
}

/**
 * DOC: driver instance overview
 *
 * A device instance for a drm driver is represented by &struct drm_device. This
 * is allocated with drm_dev_alloc(), usually from bus-specific ->probe()
 * callbacks implemented by the driver. The driver then needs to initialize all
 * the various subsystems for the drm device like memory management, vblank
 * handling, modesetting support and intial output configuration plus obviously
 * initialize all the corresponding hardware bits. An important part of this is
 * also calling drm_dev_set_unique() to set the userspace-visible unique name of
 * this device instance. Finally when everything is up and running and ready for
 * userspace the device instance can be published using drm_dev_register().
 *
 * There is also deprecated support for initalizing device instances using
 * bus-specific helpers and the &drm_driver.load callback. But due to
 * backwards-compatibility needs the device instance have to be published too
 * early, which requires unpretty global locking to make safe and is therefore
 * only support for existing drivers not yet converted to the new scheme.
 *
 * When cleaning up a device instance everything needs to be done in reverse:
 * First unpublish the device instance with drm_dev_unregister(). Then clean up
 * any other resources allocated at device initialization and drop the driver's
 * reference to &drm_device using drm_dev_put().
 *
 * Note that the lifetime rules for &drm_device instance has still a lot of
 * historical baggage. Hence use the reference counting provided by
 * drm_dev_get() and drm_dev_put() only carefully.
 *
 * It is recommended that drivers embed &struct drm_device into their own device
 * structure, which is supported through drm_dev_init().
 */

#if 0
/**
 * drm_put_dev - Unregister and release a DRM device
 * @dev: DRM device
 *
 * Called at module unload time or when a PCI device is unplugged.
 *
 * Cleans up all DRM device, calling drm_lastclose().
 *
 * Note: Use of this function is deprecated. It will eventually go away
 * completely.  Please use drm_dev_unregister() and drm_dev_put() explicitly
 * instead to make sure that the device isn't userspace accessible any more
 * while teardown is in progress, ensuring that userspace can't access an
 * inconsistent state.
 */
void drm_put_dev(struct drm_device *dev)
{
        DRM_DEBUG("\n");

        if (!dev) {
                DRM_ERROR("cleanup called no dev\n");
                return;
        }

        drm_dev_unregister(dev);
        drm_dev_put(dev);
}
EXPORT_SYMBOL(drm_put_dev);
#endif

/**
 * drm_dev_enter - Enter device critical section
 * @dev: DRM device
 * @idx: Pointer to index that will be passed to the matching drm_dev_exit()
 *
 * This function marks and protects the beginning of a section that should not
 * be entered after the device has been unplugged. The section end is marked
 * with drm_dev_exit(). Calls to this function can be nested.
 *
 * Returns:
 * True if it is OK to enter the section, false otherwise.
 */
bool drm_dev_enter(struct drm_device *dev, int *idx)
{
#if 0
        *idx = srcu_read_lock(&drm_unplug_srcu);

        if (dev->unplugged) {
                srcu_read_unlock(&drm_unplug_srcu, *idx);
                return false;
        }
#endif

        return true;
}
EXPORT_SYMBOL(drm_dev_enter);

/**
 * drm_dev_exit - Exit device critical section
 * @idx: index returned from drm_dev_enter()
 *
 * This function marks the end of a section that should not be entered after
 * the device has been unplugged.
 */
void drm_dev_exit(int idx)
{
#if 0
        srcu_read_unlock(&drm_unplug_srcu, idx);
#endif
}
EXPORT_SYMBOL(drm_dev_exit);

/**
 * drm_dev_unplug - unplug a DRM device
 * @dev: DRM device
 *
 * This unplugs a hotpluggable DRM device, which makes it inaccessible to
 * userspace operations. Entry-points can use drm_dev_enter() and
 * drm_dev_exit() to protect device resources in a race free manner. This
 * essentially unregisters the device like drm_dev_unregister(), but can be
 * called while there are still open users of @dev.
 */
void drm_dev_unplug(struct drm_device *dev)
{
        STUB();
#if 0
        /*
         * After synchronizing any critical read section is guaranteed to see
         * the new value of ->unplugged, and any critical section which might
         * still have seen the old value of ->unplugged is guaranteed to have
         * finished.
         */
        dev->unplugged = true;
        synchronize_srcu(&drm_unplug_srcu);

        drm_dev_unregister(dev);

        mutex_lock(&drm_global_mutex);
        if (dev->open_count == 0)
                drm_dev_put(dev);
        mutex_unlock(&drm_global_mutex);
#endif
}
EXPORT_SYMBOL(drm_dev_unplug);

#if 0
/*
 * DRM internal mount
 * We want to be able to allocate our own "struct address_space" to control
 * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow
 * stand-alone address_space objects, so we need an underlying inode. As there
 * is no way to allocate an independent inode easily, we need a fake internal
 * VFS mount-point.
 *
 * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free()
 * frees it again. You are allowed to use iget() and iput() to get references to
 * the inode. But each drm_fs_inode_new() call must be paired with exactly one
 * drm_fs_inode_free() call (which does not have to be the last iput()).
 * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it
 * between multiple inode-users. You could, technically, call
 * iget() + drm_fs_inode_free() directly after alloc and sometime later do an
 * iput(), but this way you'd end up with a new vfsmount for each inode.
 */

static int drm_fs_cnt;
static struct vfsmount *drm_fs_mnt;

static const struct dentry_operations drm_fs_dops = {
        .d_dname        = simple_dname,
};

static const struct super_operations drm_fs_sops = {
        .statfs         = simple_statfs,
};

static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags,
                                   const char *dev_name, void *data)
{
        return mount_pseudo(fs_type,
                            "drm:",
                            &drm_fs_sops,
                            &drm_fs_dops,
                            0x010203ff);
}

static struct file_system_type drm_fs_type = {
        .name           = "drm",
        .owner          = THIS_MODULE,
        .mount          = drm_fs_mount,
        .kill_sb        = kill_anon_super,
};

static struct inode *drm_fs_inode_new(void)
{
        struct inode *inode;
        int r;

        r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt);
        if (r < 0) {
                DRM_ERROR("Cannot mount pseudo fs: %d\n", r);
                return ERR_PTR(r);
        }

        inode = alloc_anon_inode(drm_fs_mnt->mnt_sb);
        if (IS_ERR(inode))
                simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);

        return inode;
}

static void drm_fs_inode_free(struct inode *inode)
{
        if (inode) {
                iput(inode);
                simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
        }
}
#endif

/**
 * drm_dev_init - Initialise new DRM device
 * @dev: DRM device
 * @driver: DRM driver
 * @parent: Parent device object
 *
 * Initialize a new DRM device. No device registration is done.
 * Call drm_dev_register() to advertice the device to user space and register it
 * with other core subsystems. This should be done last in the device
 * initialization sequence to make sure userspace can't access an inconsistent
 * state.
 *
 * The initial ref-count of the object is 1. Use drm_dev_get() and
 * drm_dev_put() to take and drop further ref-counts.
 *
 * Note that for purely virtual devices @parent can be NULL.
 *
 * Drivers that do not want to allocate their own device struct
 * embedding &struct drm_device can call drm_dev_alloc() instead. For drivers
 * that do embed &struct drm_device it must be placed first in the overall
 * structure, and the overall structure must be allocated using kmalloc(): The
 * drm core's release function unconditionally calls kfree() on the @dev pointer
 * when the final reference is released. To override this behaviour, and so
 * allow embedding of the drm_device inside the driver's device struct at an
 * arbitrary offset, you must supply a &drm_driver.release callback and control
 * the finalization explicitly.
 *
 * RETURNS:
 * 0 on success, or error code on failure.
 */
int drm_dev_init(struct drm_device *dev,
                 struct drm_driver *driver,
                 struct device *parent)
{
        int ret;
#ifdef __DragonFly__
        struct drm_softc *softc = device_get_softc(parent->bsddev);

        softc->drm_driver_data = dev;
#endif

        if (!drm_core_init_complete) {
                DRM_ERROR("DRM core is not initialized\n");
                return -ENODEV;
        }

        kref_init(&dev->ref);
        dev->dev = parent;
        dev->driver = driver;

        /* no per-device feature limits by default */
        dev->driver_features = ~0u;

        INIT_LIST_HEAD(&dev->filelist);
        INIT_LIST_HEAD(&dev->filelist_internal);
        INIT_LIST_HEAD(&dev->clientlist);
        INIT_LIST_HEAD(&dev->ctxlist);
        INIT_LIST_HEAD(&dev->vmalist);
        INIT_LIST_HEAD(&dev->maplist);
        INIT_LIST_HEAD(&dev->vblank_event_list);

        lockinit(&dev->buf_lock, "drmdbl", 0, 0);
        lockinit(&dev->event_lock, "drmev", 0, 0);
        lockinit(&dev->struct_mutex, "drmslk", 0, LK_CANRECURSE);
        lockinit(&dev->filelist_mutex, "drmflm", 0, LK_CANRECURSE);
        lockinit(&dev->clientlist_mutex, "drmclm", 0, LK_CANRECURSE);
        lockinit(&dev->ctxlist_mutex, "drmclm", 0, LK_CANRECURSE);
        lockinit(&dev->master_mutex, "drmmm", 0, LK_CANRECURSE);

#ifndef __DragonFly__
        dev->anon_inode = drm_fs_inode_new();
        if (IS_ERR(dev->anon_inode)) {
                ret = PTR_ERR(dev->anon_inode);
                DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret);
                goto err_free;
        }
#else
        dev->anon_inode = NULL;
        dev->pci_domain = pci_get_domain(dev->dev->bsddev);
        dev->pci_bus = pci_get_bus(dev->dev->bsddev);
        dev->pci_slot = pci_get_slot(dev->dev->bsddev);
        dev->pci_func = pci_get_function(dev->dev->bsddev);
        drm_sysctl_init(dev);
#endif

        if (drm_core_check_feature(dev, DRIVER_RENDER)) {
                ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
                if (ret)
                        goto err_minors;
        }

        ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);
        if (ret)
                goto err_minors;

        ret = drm_ht_create(&dev->map_hash, 12);
        if (ret)
                goto err_minors;

        drm_legacy_ctxbitmap_init(dev);

        if (drm_core_check_feature(dev, DRIVER_GEM)) {
                ret = drm_gem_init(dev);
                if (ret) {
                        DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
                        goto err_ctxbitmap;
                }
        }

        /* Use the parent device name as DRM device unique identifier, but fall
         * back to the driver name for virtual devices like vgem. */
#if 0
        ret = drm_dev_set_unique(dev, parent ? dev_name(parent) : driver->name);
        if (ret)
                goto err_setunique;
#endif

        return 0;

#if 0
err_setunique:
        if (drm_core_check_feature(dev, DRIVER_GEM))
                drm_gem_destroy(dev);
#endif
err_ctxbitmap:
        drm_legacy_ctxbitmap_cleanup(dev);
        drm_ht_remove(&dev->map_hash);
err_minors:
        drm_minor_free(dev, DRM_MINOR_PRIMARY);
        drm_minor_free(dev, DRM_MINOR_RENDER);
#ifndef __DragonFly__
        drm_fs_inode_free(dev->anon_inode);
err_free:
#endif
        mutex_destroy(&dev->master_mutex);
        mutex_destroy(&dev->ctxlist_mutex);
        mutex_destroy(&dev->clientlist_mutex);
        mutex_destroy(&dev->filelist_mutex);
        mutex_destroy(&dev->struct_mutex);
#ifdef __DragonFly__
        drm_sysctl_cleanup(dev);
#endif
        return ret;
}
EXPORT_SYMBOL(drm_dev_init);

/**
 * drm_dev_fini - Finalize a dead DRM device
 * @dev: DRM device
 *
 * Finalize a dead DRM device. This is the converse to drm_dev_init() and
 * frees up all data allocated by it. All driver private data should be
 * finalized first. Note that this function does not free the @dev, that is
 * left to the caller.
 *
 * The ref-count of @dev must be zero, and drm_dev_fini() should only be called
 * from a &drm_driver.release callback.
 */
void drm_dev_fini(struct drm_device *dev)
{
        drm_vblank_cleanup(dev);

        if (drm_core_check_feature(dev, DRIVER_GEM))
                drm_gem_destroy(dev);

        drm_legacy_ctxbitmap_cleanup(dev);
        drm_ht_remove(&dev->map_hash);
#if 0
        drm_fs_inode_free(dev->anon_inode);
#endif

        drm_minor_free(dev, DRM_MINOR_PRIMARY);
        drm_minor_free(dev, DRM_MINOR_RENDER);

#if 0
        put_device(dev->dev);
#endif

        mutex_destroy(&dev->master_mutex);
        mutex_destroy(&dev->ctxlist_mutex);
        mutex_destroy(&dev->clientlist_mutex);
        mutex_destroy(&dev->filelist_mutex);
        mutex_destroy(&dev->struct_mutex);
        kfree(dev->unique);
}
EXPORT_SYMBOL(drm_dev_fini);

/**
 * drm_dev_alloc - Allocate new DRM device
 * @driver: DRM driver to allocate device for
 * @parent: Parent device object
 *
 * Allocate and initialize a new DRM device. No device registration is done.
 * Call drm_dev_register() to advertice the device to user space and register it
 * with other core subsystems. This should be done last in the device
 * initialization sequence to make sure userspace can't access an inconsistent
 * state.
 *
 * The initial ref-count of the object is 1. Use drm_dev_get() and
 * drm_dev_put() to take and drop further ref-counts.
 *
 * Note that for purely virtual devices @parent can be NULL.
 *
 * Drivers that wish to subclass or embed &struct drm_device into their
 * own struct should look at using drm_dev_init() instead.
 *
 * RETURNS:
 * Pointer to new DRM device, or ERR_PTR on failure.
 */
struct drm_device *drm_dev_alloc(struct drm_driver *driver,
                                 struct device *parent)
{
        struct drm_device *dev;
        int ret;

        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev)
                return ERR_PTR(-ENOMEM);

        ret = drm_dev_init(dev, driver, parent);
        if (ret) {
                kfree(dev);
                return ERR_PTR(ret);
        }

        return dev;
}
EXPORT_SYMBOL(drm_dev_alloc);

static void drm_dev_release(struct kref *ref)
{
        struct drm_device *dev = container_of(ref, struct drm_device, ref);

        if (dev->driver->release) {
                dev->driver->release(dev);
        } else {
                drm_dev_fini(dev);
                kfree(dev);
        }
}

/**
 * drm_dev_get - Take reference of a DRM device
 * @dev: device to take reference of or NULL
 *
 * This increases the ref-count of @dev by one. You *must* already own a
 * reference when calling this. Use drm_dev_put() to drop this reference
 * again.
 *
 * This function never fails. However, this function does not provide *any*
 * guarantee whether the device is alive or running. It only provides a
 * reference to the object and the memory associated with it.
 */
void drm_dev_get(struct drm_device *dev)
{
        if (dev)
                kref_get(&dev->ref);
}
EXPORT_SYMBOL(drm_dev_get);

/**
 * drm_dev_put - Drop reference of a DRM device
 * @dev: device to drop reference of or NULL
 *
 * This decreases the ref-count of @dev by one. The device is destroyed if the
 * ref-count drops to zero.
 */
void drm_dev_put(struct drm_device *dev)
{
        if (dev)
                kref_put(&dev->ref, drm_dev_release);
}
EXPORT_SYMBOL(drm_dev_put);

/**
 * drm_dev_unref - Drop reference of a DRM device
 * @dev: device to drop reference of or NULL
 *
 * This is a compatibility alias for drm_dev_put() and should not be used by new
 * code.
 */
void drm_dev_unref(struct drm_device *dev)
{
        drm_dev_put(dev);
}
EXPORT_SYMBOL(drm_dev_unref);

static int create_compat_control_link(struct drm_device *dev)
{
        struct drm_minor *minor;
        char *name;
        int ret;

        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return 0;

        minor = *drm_minor_get_slot(dev, DRM_MINOR_PRIMARY);
        if (!minor)
                return 0;

        /*
         * Some existing userspace out there uses the existing of the controlD*
         * sysfs files to figure out whether it's a modeset driver. It only does
         * readdir, hence a symlink is sufficient (and the least confusing
         * option). Otherwise controlD* is entirely unused.
         *
         * Old controlD chardev have been allocated in the range
         * 64-127.
         */
        name = kasprintf(GFP_KERNEL, "controlD%d", minor->index + 64);
        if (!name)
                return -ENOMEM;

#ifndef __DragonFly__   /* DragonFly's libdrm does not need this */
        ret = sysfs_create_link(minor->kdev->kobj.parent,
                                &minor->kdev->kobj,
                                name);
#else
        ret = 0;
#endif

        kfree(name);

        return ret;
}

static void remove_compat_control_link(struct drm_device *dev)
{
        struct drm_minor *minor;
        char *name;

        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return;

        minor = *drm_minor_get_slot(dev, DRM_MINOR_PRIMARY);
        if (!minor)
                return;

        name = kasprintf(GFP_KERNEL, "controlD%d", minor->index + 64);
        if (!name)
                return;

#ifndef __DragonFly__
        sysfs_remove_link(minor->kdev->kobj.parent, name);
#endif

        kfree(name);
}

/**
 * drm_dev_register - Register DRM device
 * @dev: Device to register
 * @flags: Flags passed to the driver's .load() function
 *
 * Register the DRM device @dev with the system, advertise device to user-space
 * and start normal device operation. @dev must be allocated via drm_dev_alloc()
 * previously.
 *
 * Never call this twice on any device!
 *
 * NOTE: To ensure backward compatibility with existing drivers method this
 * function calls the &drm_driver.load method after registering the device
 * nodes, creating race conditions. Usage of the &drm_driver.load methods is
 * therefore deprecated, drivers must perform all initialization before calling
 * drm_dev_register().
 *
 * RETURNS:
 * 0 on success, negative error code on failure.
 */
int drm_dev_register(struct drm_device *dev, unsigned long flags)
{
        struct drm_driver *driver = dev->driver;
        int ret;

        mutex_lock(&drm_global_mutex);

        ret = drm_minor_register(dev, DRM_MINOR_RENDER);
        if (ret)
                goto err_minors;

        ret = drm_minor_register(dev, DRM_MINOR_PRIMARY);
        if (ret)
                goto err_minors;

        ret = create_compat_control_link(dev);
        if (ret)
                goto err_minors;

        dev->registered = true;

        if (dev->driver->load) {
                ret = dev->driver->load(dev, flags);
                if (ret)
                        goto err_minors;
        }

        if (drm_core_check_feature(dev, DRIVER_MODESET))
                drm_modeset_register_all(dev);

        ret = 0;

        DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
                 driver->name, driver->major, driver->minor,
                 driver->patchlevel, driver->date,
                 dev->dev ? dev_name(dev->dev) : "virtual device",
                 dev->primary->index);

        goto out_unlock;

err_minors:
        remove_compat_control_link(dev);
        drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
        drm_minor_unregister(dev, DRM_MINOR_RENDER);
out_unlock:
        mutex_unlock(&drm_global_mutex);
        return ret;
}
EXPORT_SYMBOL(drm_dev_register);

/**
 * drm_dev_unregister - Unregister DRM device
 * @dev: Device to unregister
 *
 * Unregister the DRM device from the system. This does the reverse of
 * drm_dev_register() but does not deallocate the device. The caller must call
 * drm_dev_put() to drop their final reference.
 *
 * A special form of unregistering for hotpluggable devices is drm_dev_unplug(),
 * which can be called while there are still open users of @dev.
 *
 * This should be called first in the device teardown code to make sure
 * userspace can't access the device instance any more.
 */
void drm_dev_unregister(struct drm_device *dev)
{
        struct drm_map_list *r_list, *list_temp;

        if (drm_core_check_feature(dev, DRIVER_LEGACY))
                drm_lastclose(dev);

        dev->registered = false;

#if 0 /* drm_client implementation GPL'ed until 5.7 */
        drm_client_dev_unregister(dev);
#endif

        if (drm_core_check_feature(dev, DRIVER_MODESET))
                drm_modeset_unregister_all(dev);

        if (dev->driver->unload)
                dev->driver->unload(dev);

#if 0
        if (dev->agp)
                drm_pci_agp_destroy(dev);
#endif

        list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
                drm_legacy_rmmap(dev, r_list->map);

        remove_compat_control_link(dev);
        drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
        drm_minor_unregister(dev, DRM_MINOR_RENDER);
}
EXPORT_SYMBOL(drm_dev_unregister);

#if 0
/**
 * drm_dev_set_unique - Set the unique name of a DRM device
 * @dev: device of which to set the unique name
 * @name: unique name
 *
 * Sets the unique name of a DRM device using the specified string. Drivers
 * can use this at driver probe time if the unique name of the devices they
 * drive is static.
 *
 * Return: 0 on success or a negative error code on failure.
 */
int drm_dev_set_unique(struct drm_device *dev, const char *name)
{
        kfree(dev->unique);
        dev->unique = kstrdup(name, GFP_KERNEL);

        return dev->unique ? 0 : -ENOMEM;
}
EXPORT_SYMBOL(drm_dev_set_unique);

/*
 * DRM Core
 * The DRM core module initializes all global DRM objects and makes them
 * available to drivers. Once setup, drivers can probe their respective
 * devices.
 * Currently, core management includes:
 *  - The "DRM-Global" key/value database
 *  - Global ID management for connectors
 *  - DRM major number allocation
 *  - DRM minor management
 *  - DRM sysfs class
 *  - DRM debugfs root
 *
 * Furthermore, the DRM core provides dynamic char-dev lookups. For each
 * interface registered on a DRM device, you can request minor numbers from DRM
 * core. DRM core takes care of major-number management and char-dev
 * registration. A stub ->open() callback forwards any open() requests to the
 * registered minor.
 */

static int drm_stub_open(struct inode *inode, struct file *filp)
{
        const struct file_operations *new_fops;
        struct drm_minor *minor;
        int err;

        DRM_DEBUG("\n");

        mutex_lock(&drm_global_mutex);
        minor = drm_minor_acquire(iminor(inode));
        if (IS_ERR(minor)) {
                err = PTR_ERR(minor);
                goto out_unlock;
        }

        new_fops = fops_get(minor->dev->driver->fops);
        if (!new_fops) {
                err = -ENODEV;
                goto out_release;
        }

        replace_fops(filp, new_fops);
        if (filp->f_op->open)
                err = filp->f_op->open(inode, filp);
        else
                err = 0;

out_release:
        drm_minor_release(minor);
out_unlock:
        mutex_unlock(&drm_global_mutex);
        return err;
}

static const struct file_operations drm_stub_fops = {
        .owner = THIS_MODULE,
        .open = drm_stub_open,
        .llseek = noop_llseek,
};
#endif

static void drm_core_exit(void)
{
#if 0
        unregister_chrdev(DRM_MAJOR, "drm");
        debugfs_remove(drm_debugfs_root);
        drm_sysfs_destroy();
#endif
        idr_destroy(&drm_minors_idr);
        drm_connector_ida_destroy();
        drm_global_release();
}

static int __init drm_core_init(void)
{
#if 0
        int ret;
#endif

        drm_global_init();
        drm_connector_ida_init();
        idr_init(&drm_minors_idr);

#if 0
        ret = drm_sysfs_init();
        if (ret < 0) {
                DRM_ERROR("Cannot create DRM class: %d\n", ret);
                goto error;
        }

        drm_debugfs_root = debugfs_create_dir("dri", NULL);
        if (!drm_debugfs_root) {
                ret = -ENOMEM;
                DRM_ERROR("Cannot create debugfs-root: %d\n", ret);
                goto error;
        }

        ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops);
        if (ret < 0)
                goto error;
#endif

        drm_core_init_complete = true;

        DRM_DEBUG("Initialized\n");
        return 0;

#if 0
error:
        drm_core_exit();
        return ret;
#endif
}

module_init(drm_core_init);
module_exit(drm_core_exit);

#include <sys/devfs.h>

#include <linux/export.h>
#include <linux/dmi.h>
#include <drm/drmP.h>

static int
drm_modevent(module_t mod, int type, void *data)
{

        switch (type) {
        case MOD_LOAD:
                TUNABLE_INT_FETCH("drm.debug", &drm_debug);
                linux_task_drop_callback = linux_task_drop;
                linux_proc_drop_callback = linux_proc_drop;
                break;
        case MOD_UNLOAD:
                linux_task_drop_callback = NULL;
                linux_proc_drop_callback = NULL;
                break;
        }
        return (0);
}

static moduledata_t drm_mod = {
        "drm",
        drm_modevent,
        0
};
DECLARE_MODULE(drm, drm_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
MODULE_VERSION(drm, 1);
MODULE_DEPEND(drm, agp, 1, 1, 1);
MODULE_DEPEND(drm, pci, 1, 1, 1);
MODULE_DEPEND(drm, iicbus, 1, 1, 1);

struct dev_ops drm_cdevsw = {
        { "drm", 0, D_TRACKCLOSE | D_MPSAFE },
        .d_open =       drm_open,
        .d_close =      drm_close,
        .d_read =       drm_read,
        .d_ioctl =      drm_ioctl,
        .d_kqfilter =   drm_kqfilter,
        .d_mmap =       drm_mmap,
        .d_mmap_single = drm_mmap_single,
};

SYSCTL_NODE(_hw, OID_AUTO, drm, CTLFLAG_RW, NULL, "DRM device");
SYSCTL_INT(_hw_drm, OID_AUTO, debug, CTLFLAG_RW, &drm_debug, 0,
    "DRM debugging");
int drm_vma_debug;
SYSCTL_INT(_hw_drm, OID_AUTO, vma_debug, CTLFLAG_RW, &drm_vma_debug, 0,
    "DRM debugging");

#if 0
int
drm_create_cdevs(device_t kdev)
{
        struct drm_device *dev;
        int error, unit;
#ifdef __DragonFly__
        struct drm_softc *softc = device_get_softc(kdev);

        dev = softc->drm_driver_data;
#endif
        unit = device_get_unit(kdev);

        dev->devnode = make_dev(&drm_cdevsw, unit, DRM_DEV_UID, DRM_DEV_GID,
                                DRM_DEV_MODE, "dri/card%d", unit);
        error = 0;
        if (error == 0)
                dev->devnode->si_drv1 = dev;
        return (error);
}
#endif

#ifndef DRM_DEV_NAME
#define DRM_DEV_NAME "drm"
#endif

devclass_t drm_devclass;

/* XXX: this is supposed to be drm_release() */
void drm_cdevpriv_dtor(void *cd)
{
        struct drm_file *file_priv = cd;
        struct drm_device *dev = file_priv->dev;

        DRM_DEBUG("open_count = %d\n", dev->open_count);

        DRM_LOCK(dev);

        if (dev->driver->preclose != NULL)
                dev->driver->preclose(dev, file_priv);

        /* ========================================================
         * Begin inline drm_release
         */

        DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
            DRM_CURRENTPID, (long)dev->dev, dev->open_count);

        if (dev->driver->driver_features & DRIVER_GEM)
                drm_gem_release(dev, file_priv);

        if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
                drm_legacy_reclaim_buffers(dev, file_priv);

        funsetown(&dev->buf_sigio);

        if (dev->driver->postclose != NULL)
                dev->driver->postclose(dev, file_priv);
        list_del(&file_priv->lhead);


        /* ========================================================
         * End inline drm_release
         */

        device_unbusy(dev->dev->bsddev);
        if (--dev->open_count == 0) {
                drm_lastclose(dev);
        }

        DRM_UNLOCK(dev);
}

int
drm_add_busid_modesetting(struct drm_device *dev, struct sysctl_ctx_list *ctx,
    struct sysctl_oid *top)
{
        struct sysctl_oid *oid;

        ksnprintf(dev->busid_str, sizeof(dev->busid_str),
             "pci:%04x:%02x:%02x.%d", dev->pci_domain, dev->pci_bus,
             dev->pci_slot, dev->pci_func);
        oid = SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(top), OID_AUTO, "busid",
            CTLFLAG_RD, dev->busid_str, 0, NULL);
        if (oid == NULL)
                return (ENOMEM);

        return (0);
}

int
drm_mmap_single(struct dev_mmap_single_args *ap)
{
        struct drm_device *dev;
        struct cdev *kdev = ap->a_head.a_dev;
        vm_ooffset_t *offset = ap->a_offset;
        vm_size_t size = ap->a_size;
        struct vm_object **obj_res = ap->a_object;
        int nprot = ap->a_nprot;

        dev = drm_get_device_from_kdev(kdev);
        if (dev->drm_ttm_bdev != NULL) {
                return (ttm_bo_mmap_single(ap->a_fp, dev,
                                           offset, size, obj_res, nprot));
        } else if ((dev->driver->driver_features & DRIVER_GEM) != 0) {
                return (drm_gem_mmap_single(dev, offset, size, obj_res, nprot));
        } else {
                return (ENODEV);
        }
}

#include <linux/dmi.h>

/*
 * Check if dmi_system_id structure matches system DMI data
 */
static bool
dmi_found(const struct dmi_system_id *dsi)
{
        int i, slot;
        bool found = false;
        char *sys_vendor, *board_vendor, *product_name, *board_name;

        sys_vendor = kgetenv("smbios.system.maker");
        board_vendor = kgetenv("smbios.planar.maker");
        product_name = kgetenv("smbios.system.product");
        board_name = kgetenv("smbios.planar.product");

        for (i = 0; i < NELEM(dsi->matches); i++) {
                slot = dsi->matches[i].slot;
                switch (slot) {
                case DMI_NONE:
                        break;
                case DMI_SYS_VENDOR:
                        if (sys_vendor != NULL &&
                            !strcmp(sys_vendor, dsi->matches[i].substr))
                                break;
                        else
                                goto done;
                case DMI_BOARD_VENDOR:
                        if (board_vendor != NULL &&
                            !strcmp(board_vendor, dsi->matches[i].substr))
                                break;
                        else
                                goto done;
                case DMI_PRODUCT_NAME:
                        if (product_name != NULL &&
                            !strcmp(product_name, dsi->matches[i].substr))
                                break;
                        else
                                goto done;
                case DMI_BOARD_NAME:
                        if (board_name != NULL &&
                            !strcmp(board_name, dsi->matches[i].substr))
                                break;
                        else
                                goto done;
                default:
                        goto done;
                }
        }
        found = true;

done:
        if (sys_vendor != NULL)
                kfreeenv(sys_vendor);
        if (board_vendor != NULL)
                kfreeenv(board_vendor);
        if (product_name != NULL)
                kfreeenv(product_name);
        if (board_name != NULL)
                kfreeenv(board_name);

        return found;
}

int dmi_check_system(const struct dmi_system_id *sysid)
{
        const struct dmi_system_id *dsi;
        int num = 0;

        for (dsi = sysid; dsi->matches[0].slot != 0 ; dsi++) {
                if (dmi_found(dsi)) {
                        num++;
                        if (dsi->callback && dsi->callback(dsi))
                                break;
                }
        }
        return (num);
}