drm_pci.c (7315B)
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