linux/drivers/gpu/drm/drm_pci.c
<<
>>
Prefs
   1/*
   2 * Copyright 2003 José Fonseca.
   3 * Copyright 2003 Leif Delgass.
   4 * All Rights Reserved.
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a
   7 * copy of this software and associated documentation files (the "Software"),
   8 * to deal in the Software without restriction, including without limitation
   9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  10 * and/or sell copies of the Software, and to permit persons to whom the
  11 * Software is furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice (including the next
  14 * paragraph) shall be included in all copies or substantial portions of the
  15 * Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  20 * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23 */
  24
  25#include <linux/dma-mapping.h>
  26#include <linux/export.h>
  27#include <linux/list.h>
  28#include <linux/mutex.h>
  29#include <linux/pci.h>
  30#include <linux/slab.h>
  31
  32#include <drm/drm.h>
  33#include <drm/drm_drv.h>
  34#include <drm/drm_print.h>
  35
  36#include "drm_internal.h"
  37#include "drm_legacy.h"
  38
  39#ifdef CONFIG_DRM_LEGACY
  40/* List of devices hanging off drivers with stealth attach. */
  41static LIST_HEAD(legacy_dev_list);
  42static DEFINE_MUTEX(legacy_dev_list_lock);
  43#endif
  44
  45static int drm_get_pci_domain(struct drm_device *dev)
  46{
  47#ifndef __alpha__
  48        /* For historical reasons, drm_get_pci_domain() is busticated
  49         * on most archs and has to remain so for userspace interface
  50         * < 1.4, except on alpha which was right from the beginning
  51         */
  52        if (dev->if_version < 0x10004)
  53                return 0;
  54#endif /* __alpha__ */
  55
  56        return pci_domain_nr(to_pci_dev(dev->dev)->bus);
  57}
  58
  59int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
  60{
  61        struct pci_dev *pdev = to_pci_dev(dev->dev);
  62
  63        master->unique = kasprintf(GFP_KERNEL, "pci:%04x:%02x:%02x.%d",
  64                                        drm_get_pci_domain(dev),
  65                                        pdev->bus->number,
  66                                        PCI_SLOT(pdev->devfn),
  67                                        PCI_FUNC(pdev->devfn));
  68        if (!master->unique)
  69                return -ENOMEM;
  70
  71        master->unique_len = strlen(master->unique);
  72        return 0;
  73}
  74
  75#ifdef CONFIG_DRM_LEGACY
  76
  77static int drm_legacy_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
  78{
  79        struct pci_dev *pdev = to_pci_dev(dev->dev);
  80
  81        if ((p->busnum >> 8) != drm_get_pci_domain(dev) ||
  82            (p->busnum & 0xff) != pdev->bus->number ||
  83            p->devnum != PCI_SLOT(pdev->devfn) || p->funcnum != PCI_FUNC(pdev->devfn))
  84                return -EINVAL;
  85
  86        p->irq = pdev->irq;
  87
  88        DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum,
  89                  p->irq);
  90        return 0;
  91}
  92
  93/**
  94 * drm_legacy_irq_by_busid - Get interrupt from bus ID
  95 * @dev: DRM device
  96 * @data: IOCTL parameter pointing to a drm_irq_busid structure
  97 * @file_priv: DRM file private.
  98 *
  99 * Finds the PCI device with the specified bus id and gets its IRQ number.
 100 * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
 101 * to that of the device that this DRM instance attached to.
 102 *
 103 * Return: 0 on success or a negative error code on failure.
 104 */
 105int drm_legacy_irq_by_busid(struct drm_device *dev, void *data,
 106                            struct drm_file *file_priv)
 107{
 108        struct drm_irq_busid *p = data;
 109
 110        if (!drm_core_check_feature(dev, DRIVER_LEGACY))
 111                return -EOPNOTSUPP;
 112
 113        /* UMS was only ever support on PCI devices. */
 114        if (WARN_ON(!dev_is_pci(dev->dev)))
 115                return -EINVAL;
 116
 117        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
 118                return -EOPNOTSUPP;
 119
 120        return drm_legacy_pci_irq_by_busid(dev, p);
 121}
 122
 123void drm_legacy_pci_agp_destroy(struct drm_device *dev)
 124{
 125        if (dev->agp) {
 126                arch_phys_wc_del(dev->agp->agp_mtrr);
 127                drm_legacy_agp_clear(dev);
 128                kfree(dev->agp);
 129                dev->agp = NULL;
 130        }
 131}
 132
 133static void drm_legacy_pci_agp_init(struct drm_device *dev)
 134{
 135        if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
 136                if (pci_find_capability(to_pci_dev(dev->dev), PCI_CAP_ID_AGP))
 137                        dev->agp = drm_legacy_agp_init(dev);
 138                if (dev->agp) {
 139                        dev->agp->agp_mtrr = arch_phys_wc_add(
 140                                dev->agp->agp_info.aper_base,
 141                                dev->agp->agp_info.aper_size *
 142                                1024 * 1024);
 143                }
 144        }
 145}
 146
 147static int drm_legacy_get_pci_dev(struct pci_dev *pdev,
 148                                  const struct pci_device_id *ent,
 149                                  const struct drm_driver *driver)
 150{
 151        struct drm_device *dev;
 152        int ret;
 153
 154        DRM_DEBUG("\n");
 155
 156        dev = drm_dev_alloc(driver, &pdev->dev);
 157        if (IS_ERR(dev))
 158                return PTR_ERR(dev);
 159
 160        ret = pci_enable_device(pdev);
 161        if (ret)
 162                goto err_free;
 163
 164#ifdef __alpha__
 165        dev->hose = pdev->sysdata;
 166#endif
 167
 168        drm_legacy_pci_agp_init(dev);
 169
 170        ret = drm_dev_register(dev, ent->driver_data);
 171        if (ret)
 172                goto err_agp;
 173
 174        if (drm_core_check_feature(dev, DRIVER_LEGACY)) {
 175                mutex_lock(&legacy_dev_list_lock);
 176                list_add_tail(&dev->legacy_dev_list, &legacy_dev_list);
 177                mutex_unlock(&legacy_dev_list_lock);
 178        }
 179
 180        return 0;
 181
 182err_agp:
 183        drm_legacy_pci_agp_destroy(dev);
 184        pci_disable_device(pdev);
 185err_free:
 186        drm_dev_put(dev);
 187        return ret;
 188}
 189
 190/**
 191 * drm_legacy_pci_init - shadow-attach a legacy DRM PCI driver
 192 * @driver: DRM device driver
 193 * @pdriver: PCI device driver
 194 *
 195 * This is only used by legacy dri1 drivers and deprecated.
 196 *
 197 * Return: 0 on success or a negative error code on failure.
 198 */
 199int drm_legacy_pci_init(const struct drm_driver *driver,
 200                        struct pci_driver *pdriver)
 201{
 202        struct pci_dev *pdev = NULL;
 203        const struct pci_device_id *pid;
 204        int i;
 205
 206        DRM_DEBUG("\n");
 207
 208        if (WARN_ON(!(driver->driver_features & DRIVER_LEGACY)))
 209                return -EINVAL;
 210
 211        /* If not using KMS, fall back to stealth mode manual scanning. */
 212        for (i = 0; pdriver->id_table[i].vendor != 0; i++) {
 213                pid = &pdriver->id_table[i];
 214
 215                /* Loop around setting up a DRM device for each PCI device
 216                 * matching our ID and device class.  If we had the internal
 217                 * function that pci_get_subsys and pci_get_class used, we'd
 218                 * be able to just pass pid in instead of doing a two-stage
 219                 * thing.
 220                 */
 221                pdev = NULL;
 222                while ((pdev =
 223                        pci_get_subsys(pid->vendor, pid->device, pid->subvendor,
 224                                       pid->subdevice, pdev)) != NULL) {
 225                        if ((pdev->class & pid->class_mask) != pid->class)
 226                                continue;
 227
 228                        /* stealth mode requires a manual probe */
 229                        pci_dev_get(pdev);
 230                        drm_legacy_get_pci_dev(pdev, pid, driver);
 231                }
 232        }
 233        return 0;
 234}
 235EXPORT_SYMBOL(drm_legacy_pci_init);
 236
 237/**
 238 * drm_legacy_pci_exit - unregister shadow-attach legacy DRM driver
 239 * @driver: DRM device driver
 240 * @pdriver: PCI device driver
 241 *
 242 * Unregister a DRM driver shadow-attached through drm_legacy_pci_init(). This
 243 * is deprecated and only used by dri1 drivers.
 244 */
 245void drm_legacy_pci_exit(const struct drm_driver *driver,
 246                         struct pci_driver *pdriver)
 247{
 248        struct drm_device *dev, *tmp;
 249
 250        DRM_DEBUG("\n");
 251
 252        if (!(driver->driver_features & DRIVER_LEGACY)) {
 253                WARN_ON(1);
 254        } else {
 255                mutex_lock(&legacy_dev_list_lock);
 256                list_for_each_entry_safe(dev, tmp, &legacy_dev_list,
 257                                         legacy_dev_list) {
 258                        if (dev->driver == driver) {
 259                                list_del(&dev->legacy_dev_list);
 260                                drm_put_dev(dev);
 261                        }
 262                }
 263                mutex_unlock(&legacy_dev_list_lock);
 264        }
 265        DRM_INFO("Module unloaded\n");
 266}
 267EXPORT_SYMBOL(drm_legacy_pci_exit);
 268
 269#endif
 270