1494N/A/*
1494N/A * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
1494N/A */
1494N/A
1494N/A/*
1494N/A * Copyright (c) 2012, 2013 Intel Corporation. All rights reserved.
1494N/A */
1494N/A
1494N/A/**
1494N/A * \file drm_stub.h
1494N/A * Stub support
1494N/A *
1494N/A * \author Rickard E. (Rik) Faith <faith@valinux.com>
1494N/A */
1494N/A
1494N/A/*
1494N/A * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
1494N/A *
1494N/A * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
1494N/A * All Rights Reserved.
1494N/A *
1494N/A * Permission is hereby granted, free of charge, to any person obtaining a
1494N/A * copy of this software and associated documentation files (the "Software"),
1494N/A * to deal in the Software without restriction, including without limitation
1494N/A * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1494N/A * and/or sell copies of the Software, and to permit persons to whom the
1494N/A * Software is furnished to do so, subject to the following conditions:
1494N/A *
1494N/A * The above copyright notice and this permission notice (including the next
1494N/A * paragraph) shall be included in all copies or substantial portions of the
1494N/A * Software.
1494N/A *
1494N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1494N/A * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1494N/A * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1494N/A * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1494N/A * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1494N/A * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1494N/A * DEALINGS IN THE SOFTWARE.
1494N/A */
1494N/A
1494N/A#include "drmP.h"
1494N/A#include "drm_core.h"
1494N/A#include "drm_linux_list.h"
1494N/A
1494N/Aunsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
1494N/Aunsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */
1494N/A
1494N/Astruct idr drm_minors_idr;
1494N/A
1494N/Astatic int drm_minor_get_id(struct drm_device *dev, int type)
1494N/A{
1494N/A int new_id;
1494N/A int ret;
1494N/A int base, limit;
1494N/A
1494N/A switch (type) {
1494N/A case DRM_MINOR_LEGACY:
1494N/A base = DRM_MINOR_ID_BASE_LEGACY;
1494N/A limit = DRM_MINOR_ID_LIMIT_LEGACY;
1494N/A break;
1494N/A case DRM_MINOR_CONTROL:
1494N/A base = DRM_MINOR_ID_BASE_CONTROL;
1494N/A limit = DRM_MINOR_ID_LIMIT_CONTROL;
1494N/A break;
1494N/A case DRM_MINOR_RENDER:
1494N/A base = DRM_MINOR_ID_BASE_RENDER;
1494N/A limit = DRM_MINOR_ID_LIMIT_RENDER;
1494N/A break;
1494N/A case DRM_MINOR_VGATEXT:
1494N/A base = DRM_MINOR_ID_BASE_VGATEXT;
1494N/A limit = DRM_MINOR_ID_LIMIT_VGATEXT;
1494N/A break;
1494N/A case DRM_MINOR_AGPMASTER:
1494N/A base = DRM_MINOR_ID_BASE_AGPMASTER;
1494N/A limit = DRM_MINOR_ID_LIMIT_AGPMASTER;
1494N/A break;
1494N/A default:
1494N/A return -EINVAL;
1494N/A }
1494N/A
1494N/A mutex_lock(&dev->struct_mutex);
1494N/A ret = idr_get_new_above(&drm_minors_idr, NULL,
1494N/A base, &new_id);
1494N/A mutex_unlock(&dev->struct_mutex);
1494N/A
1494N/A if (ret) {
1494N/A return ret;
1494N/A }
1494N/A
1494N/A if (new_id > limit) {
1494N/A (void) idr_remove(&drm_minors_idr, new_id);
1494N/A return -EINVAL;
1494N/A }
1494N/A return new_id;
1494N/A}
1494N/A
1494N/Astruct drm_master *drm_master_create(struct drm_minor *minor)
1494N/A{
1494N/A struct drm_master *master;
1494N/A int i;
1494N/A
1494N/A master = kzalloc(sizeof(*master), GFP_KERNEL);
1494N/A if (!master)
1494N/A return NULL;
1494N/A
1494N/A kref_init(&master->refcount);
1494N/A mutex_init(&master->lock.lock_mutex, NULL, MUTEX_DRIVER, (void *)minor->dev->pdev->intr_block);
1494N/A cv_init(&master->lock.lock_cv, NULL, CV_DRIVER, NULL);
1494N/A
1494N/A for (i = 0; i < DRM_HASH_SIZE; i++) {
1494N/A master->magiclist[i].head = NULL;
1494N/A master->magiclist[i].tail = NULL;
1494N/A }
1494N/A
1494N/A master->minor = minor;
1494N/A
1494N/A list_add_tail(&master->head, &minor->master_list, (caddr_t)master);
1494N/A
1494N/A return master;
1494N/A}
1494N/A
1494N/Astruct drm_master *drm_master_get(struct drm_master *master)
1494N/A{
1494N/A kref_get(&master->refcount);
1494N/A return master;
1494N/A}
1494N/A
1494N/Avoid drm_master_destroy(struct kref *kref)
1494N/A{
1494N/A struct drm_master *master = container_of(kref, struct drm_master, refcount);
1494N/A struct drm_magic_entry *pt, *next;
1494N/A struct drm_device *dev = master->minor->dev;
1494N/A struct drm_map_list *r_list, *list_temp;
1494N/A int i;
1494N/A
1494N/A list_del(&master->head);
1494N/A
1494N/A if (dev->driver->master_destroy)
1494N/A dev->driver->master_destroy(dev, master);
1494N/A
1494N/A list_for_each_entry_safe(r_list, list_temp, struct drm_map_list, &dev->maplist, head) {
1494N/A if (r_list->master == master) {
1494N/A (void) drm_rmmap_locked(dev, r_list->map);
1494N/A r_list = NULL;
1494N/A }
1494N/A }
1494N/A
1494N/A if (master->unique) {
1494N/A kfree(master->unique, master->unique_size);
1494N/A master->unique = NULL;
1494N/A master->unique_len = 0;
1494N/A
1494N/A }
1494N/A
1494N/A for (i = 0; i < DRM_HASH_SIZE; i++) {
1494N/A for (pt = master->magiclist[i].head; pt; pt = next) {
1494N/A next = pt->next;
1494N/A drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
1494N/A }
1494N/A master->magiclist[i].head = master->magiclist[i].tail = NULL;
1494N/A }
1494N/A
1494N/A cv_destroy(&master->lock.lock_cv);
1494N/A mutex_destroy(&master->lock.lock_mutex);
1494N/A
1494N/A kfree(master, sizeof (struct drm_master));
1494N/A}
1494N/A
1494N/Avoid drm_master_put(struct drm_master **master)
1494N/A{
1494N/A kref_put(&(*master)->refcount, drm_master_destroy);
1494N/A *master = NULL;
1494N/A}
1494N/A
1494N/A/* LINTED */
1494N/Aint drm_setmaster_ioctl(DRM_IOCTL_ARGS)
1494N/A{
1494N/A int ret = 0;
1494N/A
1494N/A if (dev->driver->entervt)
1494N/A dev->driver->entervt(dev);
1494N/A
1494N/A if (file->is_master)
1494N/A return 0;
1494N/A
1494N/A if (file->minor->master && file->minor->master != file->master)
1494N/A return -EINVAL;
1494N/A
1494N/A if (!file->master)
1494N/A return -EINVAL;
1494N/A
1494N/A if (!file->minor->master &&
1494N/A file->minor->master != file->master) {
1494N/A mutex_lock(&dev->struct_mutex);
1494N/A file->minor->master = drm_master_get(file->master);
1494N/A file->is_master = 1;
1494N/A if (dev->driver->master_set) {
1494N/A ret = dev->driver->master_set(dev, file, false);
1494N/A if (unlikely(ret != 0)) {
1494N/A file->is_master = 0;
1494N/A drm_master_put(&file->minor->master);
1494N/A }
1494N/A }
1494N/A mutex_unlock(&dev->struct_mutex);
1494N/A }
1494N/A
1494N/A return 0;
1494N/A}
1494N/A
1494N/A/* LINTED */
1494N/Aint drm_dropmaster_ioctl(DRM_IOCTL_ARGS)
1494N/A{
1494N/A if (!file->is_master)
1494N/A return -EINVAL;
1494N/A
1494N/A if (!file->minor->master)
1494N/A return -EINVAL;
1494N/A
1494N/A if (dev->driver->leavevt)
1494N/A dev->driver->leavevt(dev);
1494N/A
1494N/A mutex_lock(&dev->struct_mutex);
1494N/A if (dev->driver->master_drop)
1494N/A dev->driver->master_drop(dev, file, false);
1494N/A drm_master_put(&file->minor->master);
1494N/A file->is_master = 0;
1494N/A mutex_unlock(&dev->struct_mutex);
1494N/A return 0;
1494N/A}
1494N/A
1494N/Astatic int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
1494N/A struct drm_driver *driver)
1494N/A{
1494N/A int retcode;
1494N/A
1494N/A INIT_LIST_HEAD(&dev->filelist);
1494N/A INIT_LIST_HEAD(&dev->ctxlist);
1494N/A INIT_LIST_HEAD(&dev->maplist);
1494N/A INIT_LIST_HEAD(&dev->vblank_event_list);
1494N/A INIT_LIST_HEAD(&dev->gem_objects_list);
1494N/A
1494N/A mutex_init(&dev->count_lock, NULL, MUTEX_DRIVER, (void *)pdev->intr_block);
1494N/A mutex_init(&dev->event_lock, NULL, MUTEX_DRIVER, (void *)pdev->intr_block);
1494N/A mutex_init(&dev->struct_mutex, NULL, MUTEX_DRIVER, NULL); //adaptive locks
1494N/A mutex_init(&dev->ctxlist_mutex, NULL, MUTEX_DRIVER, NULL);
1494N/A mutex_init(&dev->irq_lock, NULL, MUTEX_DRIVER, (void *)pdev->intr_block);
1494N/A mutex_init(&dev->track_lock, NULL, MUTEX_DRIVER, (void *)pdev->intr_block);
1494N/A mutex_init(&dev->page_fault_lock, NULL, MUTEX_DRIVER, NULL);
1494N/A
1494N/A dev->pdev = pdev;
1494N/A dev->pci_device = pdev->device;
1494N/A dev->pci_vendor = pdev->vendor;
1494N/A
1494N/A idr_init(&dev->map_idr);
1494N/A
1494N/A /* the DRM has 6 basic counters */
1494N/A dev->counters = 6;
1494N/A dev->types[0] = _DRM_STAT_LOCK;
1494N/A dev->types[1] = _DRM_STAT_OPENS;
1494N/A dev->types[2] = _DRM_STAT_CLOSES;
1494N/A dev->types[3] = _DRM_STAT_IOCTLS;
1494N/A dev->types[4] = _DRM_STAT_LOCKS;
1494N/A dev->types[5] = _DRM_STAT_UNLOCKS;
1494N/A
1494N/A dev->driver = driver;
1494N/A
1494N/A retcode = drm_ctxbitmap_init(dev);
1494N/A if (retcode) {
1494N/A DRM_ERROR("Cannot allocate memory for context bitmap.\n");
1494N/A goto error_out_unreg;
1494N/A }
1494N/A
1494N/A if (driver->driver_features & DRIVER_GEM) {
1494N/A retcode = drm_gem_init(dev);
1494N/A if (retcode) {
1494N/A DRM_ERROR("Cannot initialize graphics execution "
1494N/A "manager (GEM)\n");
1494N/A goto error_out_unreg;
1494N/A }
1494N/A }
1494N/A
1494N/A dev->drm_wq = create_workqueue(dev->devinfo, "drm");
1494N/A if (dev->drm_wq == NULL) {
1494N/A DRM_ERROR("Failed to create drm workqueue.\n");
1494N/A goto error_out_unreg;
1494N/A }
1494N/A
1494N/A return 0;
1494N/A
1494N/A error_out_unreg:
1494N/A (void)drm_lastclose(dev);
1494N/A return retcode;
1494N/A}
1494N/A
1494N/A
1494N/A/**
1494N/A * Get a secondary minor number.
1494N/A *
1494N/A * \param dev device data structure
1494N/A * \param sec-minor structure to hold the assigned minor
1494N/A * \return negative number on failure.
1494N/A *
1494N/A * Search an empty entry and initialize it to the given parameters, and
1494N/A * create the proc init entry via proc_init(). This routines assigns
1494N/A * minor numbers to secondary heads of multi-headed cards
1494N/A */
1494N/Astatic int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
1494N/A{
1494N/A struct drm_minor *new_minor;
1494N/A int ret;
1494N/A int minor_id;
1494N/A
1494N/A DRM_DEBUG("\n");
1494N/A
1494N/A minor_id = drm_minor_get_id(dev, type);
1494N/A if (minor_id < 0)
1494N/A return minor_id;
1494N/A
1494N/A new_minor = kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
1494N/A if (!new_minor) {
1494N/A ret = -ENOMEM;
1494N/A goto err_idr;
1494N/A }
1494N/A
1494N/A new_minor->type = type;
1494N/A new_minor->dev = dev;
1494N/A new_minor->index = minor_id;
1494N/A INIT_LIST_HEAD(&new_minor->master_list);
1494N/A
1494N/A (void) idr_replace(&drm_minors_idr, new_minor, minor_id);
1494N/A
1494N/A if (type == DRM_MINOR_LEGACY)
1494N/A (void) sprintf(new_minor->name, "drm%d", new_minor->index);
1494N/A else if (type == DRM_MINOR_CONTROL)
1494N/A (void) sprintf(new_minor->name, "controlD%d", new_minor->index);
1494N/A else if (type == DRM_MINOR_RENDER)
1494N/A (void) sprintf(new_minor->name, "renderD%d", new_minor->index);
1494N/A else if (type == DRM_MINOR_VGATEXT)
1494N/A (void) sprintf(new_minor->name, "gfx%d", new_minor->index - DRM_MINOR_ID_BASE_VGATEXT);
1494N/A else if (type == DRM_MINOR_AGPMASTER)
1494N/A (void) sprintf(new_minor->name, "agpmaster%d", new_minor->index - DRM_MINOR_ID_BASE_AGPMASTER);
1494N/A
1494N/A idr_init(&new_minor->clone_idr);
1494N/A
1494N/A ret = drm_sysfs_device_add(new_minor);
1494N/A if (ret)
1494N/A goto err_g2;
1494N/A *minor = new_minor;
1494N/A
1494N/A DRM_DEBUG("new minor assigned %d\n", minor_id);
1494N/A return 0;
1494N/A
1494N/A
1494N/Aerr_g2:
1494N/Aerr_mem:
1494N/A kfree(new_minor, sizeof (*new_minor));
1494N/Aerr_idr:
1494N/A (void) idr_remove(&drm_minors_idr, minor_id);
1494N/A *minor = NULL;
1494N/A return ret;
1494N/A}
1494N/A
1494N/A/**
1494N/A * Register.
1494N/A *
1494N/A * \return zero on success or a negative number on failure.
1494N/A *
1494N/A * Attempt to gets inter module "drm" information. If we are first
1494N/A * then register the character device and inter module information.
1494N/A * Try and register, if we fail to register, backout previous work.
1494N/A */
1494N/Aint drm_get_dev(struct drm_device *dev, struct pci_dev *pdev,
1494N/A struct drm_driver *driver, unsigned long driver_data)
1494N/A{
1494N/A int ret;
1494N/A
1494N/A DRM_DEBUG("\n");
1494N/A
1494N/A if ((ret = drm_fill_in_dev(dev, pdev, driver))) {
1494N/A DRM_ERROR("DRM: Fill_in_dev failed");
1494N/A goto err_g1;
1494N/A }
1494N/A
1494N/A if ((ret = drm_get_minor(dev, &dev->vgatext, DRM_MINOR_VGATEXT))) {
1494N/A goto err_g2;
1494N/A }
1494N/A
1494N/A if (dev->driver->agp_support_detect)
1494N/A dev->driver->agp_support_detect(dev, driver_data);
1494N/A
1494N/A if (drm_core_has_AGP(dev)) {
1494N/A if ((ret = drm_get_minor(dev, &dev->agpmaster, DRM_MINOR_AGPMASTER)))
1494N/A goto err_g1;
1494N/A }
1494N/A
1494N/A if (drm_core_check_feature(dev, DRIVER_MODESET)) {
1494N/A ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
1494N/A if (ret)
1494N/A goto err_g3;
1494N/A }
1494N/A
1494N/A if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
1494N/A goto err_g4;
1494N/A
1494N/A if (dev->driver->load) {
1494N/A ret = dev->driver->load(dev, driver_data);
1494N/A if (ret)
1494N/A goto err_g5;
1494N/A }
1494N/A
1494N/A if (drm_init_kstats(dev)) {
1494N/A DRM_ERROR("init kstats error");
1494N/A ret = EFAULT;
1494N/A goto err_g5;
1494N/A }
1494N/A
1494N/A cmn_err(CE_CONT, "!Initialized %s v%d.%d.%d Modified date %s",
1494N/A driver->name, driver->major, driver->minor, driver->patchlevel,
1494N/A driver->date);
1494N/A
1494N/A return 0;
1494N/A
1494N/Aerr_g5:
1494N/A (void) drm_put_minor(&dev->primary);
1494N/Aerr_g4:
1494N/A if (drm_core_check_feature(dev, DRIVER_MODESET))
1494N/A (void) drm_put_minor(&dev->control);
1494N/Aerr_g3:
1494N/A (void) drm_put_minor(&dev->vgatext);
1494N/Aerr_g2:
1494N/A if (drm_core_has_AGP(dev))
1494N/A (void) drm_put_minor(&dev->agpmaster);
1494N/Aerr_g1:
1494N/A return ret;
1494N/A}
1494N/A
1494N/A/**
1494N/A * Put a secondary minor number.
1494N/A *
1494N/A * \param sec_minor - structure to be released
1494N/A * \return always zero
1494N/A *
1494N/A * Cleans up the proc resources. Not legal for this to be the
1494N/A * last minor released.
1494N/A *
1494N/A */
1494N/Aint drm_put_minor(struct drm_minor **minor_p)
1494N/A{
1494N/A struct drm_minor *minor = *minor_p;
1494N/A
1494N/A DRM_DEBUG("release secondary minor %d\n", minor->index);
1494N/A
1494N/A drm_sysfs_device_remove(minor);
1494N/A
1494N/A (void) idr_remove(&drm_minors_idr, minor->index);
1494N/A
1494N/A idr_destroy(&minor->clone_idr);
1494N/A
1494N/A kfree(minor, sizeof (*minor));
1494N/A *minor_p = NULL;
1494N/A return 0;
1494N/A}
1494N/A
1494N/A/**
1494N/A * Called via drm_exit() at module unload time or when pci device is
1494N/A * unplugged.
1494N/A *
1494N/A * Cleans up all DRM device, calling drm_lastclose().
1494N/A *
1494N/A * \sa drm_init
1494N/A */
1494N/Avoid drm_put_dev(struct drm_device *dev)
1494N/A{
1494N/A struct drm_driver *driver;
1494N/A struct drm_map_list *r_list, *list_temp;
1494N/A
1494N/A DRM_DEBUG("\n");
1494N/A
1494N/A if (!dev) {
1494N/A DRM_ERROR("cleanup called no dev\n");
1494N/A return;
1494N/A }
1494N/A driver = dev->driver;
1494N/A
1494N/A (void) drm_lastclose(dev);
1494N/A
1494N/A (void) destroy_workqueue(dev->drm_wq);
1494N/A
1494N/A if (dev->driver->unload)
1494N/A dev->driver->unload(dev);
1494N/A
1494N/A gfxp_mempool_destroy();
1494N/A
1494N/A if (drm_core_has_AGP(dev) && dev->agp) {
1494N/A drm_agp_cleanup(dev);
1494N/A kfree(dev->agp, sizeof(*dev->agp));
1494N/A dev->agp = NULL;
1494N/A }
1494N/A
1494N/A drm_vblank_cleanup(dev);
1494N/A
1494N/A list_for_each_entry_safe(r_list, list_temp, struct drm_map_list, &dev->maplist, head)
1494N/A (void) drm_rmmap(dev, r_list->map);
1494N/A idr_destroy(&dev->map_idr);
1494N/A
1494N/A drm_ctxbitmap_cleanup(dev);
1494N/A
1494N/A (void) drm_put_minor(&dev->vgatext);
1494N/A
1494N/A if (drm_core_has_AGP(dev))
1494N/A (void) drm_put_minor(&dev->agpmaster);
1494N/A
1494N/A if (drm_core_check_feature(dev, DRIVER_MODESET))
1494N/A (void) drm_put_minor(&dev->control);
1494N/A
1494N/A if (driver->driver_features & DRIVER_GEM)
1494N/A drm_gem_destroy(dev);
1494N/A
1494N/A (void) drm_put_minor(&dev->primary);
1494N/A
1494N/A mutex_destroy(&dev->irq_lock);
1494N/A mutex_destroy(&dev->ctxlist_mutex);
1494N/A mutex_destroy(&dev->struct_mutex);
1494N/A mutex_destroy(&dev->event_lock);
1494N/A mutex_destroy(&dev->count_lock);
1494N/A
1494N/A drm_fini_kstats(dev);
1494N/A}