Home | History | Annotate | Line # | Download | only in drm
      1 /*	$NetBSD: drm_pci.c,v 1.7 2021/12/18 23:44:57 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2003 Jos Fonseca.
      5  * Copyright 2003 Leif Delgass.
      6  * All Rights Reserved.
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the "Software"),
     10  * to deal in the Software without restriction, including without limitation
     11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     12  * and/or sell copies of the Software, and to permit persons to whom the
     13  * Software is furnished to do so, subject to the following conditions:
     14  *
     15  * The above copyright notice and this permission notice (including the next
     16  * paragraph) shall be included in all copies or substantial portions of the
     17  * Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     22  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     24  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  */
     26 
     27 #include <sys/cdefs.h>
     28 __KERNEL_RCSID(0, "$NetBSD: drm_pci.c,v 1.7 2021/12/18 23:44:57 riastradh Exp $");
     29 
     30 #include <linux/dma-mapping.h>
     31 #include <linux/export.h>
     32 #include <linux/pci.h>
     33 #include <linux/slab.h>
     34 
     35 #include <drm/drm.h>
     36 #include <drm/drm_agpsupport.h>
     37 #include <drm/drm_drv.h>
     38 #include <drm/drm_pci.h>
     39 #include <drm/drm_print.h>
     40 
     41 #include "drm_internal.h"
     42 #include "drm_legacy.h"
     43 
     44 /**
     45  * drm_pci_alloc - Allocate a PCI consistent memory block, for DMA.
     46  * @dev: DRM device
     47  * @size: size of block to allocate
     48  * @align: alignment of block
     49  *
     50  * FIXME: This is a needless abstraction of the Linux dma-api and should be
     51  * removed.
     52  *
     53  * Return: A handle to the allocated memory block on success or NULL on
     54  * failure.
     55  */
     56 drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t align)
     57 {
     58 	drm_dma_handle_t *dmah;
     59 	unsigned long addr;
     60 	size_t sz;
     61 
     62 	/* pci_alloc_consistent only guarantees alignment to the smallest
     63 	 * PAGE_SIZE order which is greater than or equal to the requested size.
     64 	 * Return NULL here for now to make sure nobody tries for larger alignment
     65 	 */
     66 	if (align > size)
     67 		return NULL;
     68 
     69 	dmah = kmalloc(sizeof(drm_dma_handle_t), GFP_KERNEL);
     70 	if (!dmah)
     71 		return NULL;
     72 
     73 	dmah->size = size;
     74 	dmah->vaddr = dma_alloc_coherent(&dev->pdev->dev, size,
     75 					 &dmah->busaddr,
     76 					 GFP_KERNEL | __GFP_COMP);
     77 
     78 	if (dmah->vaddr == NULL) {
     79 		kfree(dmah);
     80 		return NULL;
     81 	}
     82 
     83 	/* XXX - Is virt_to_page() legal for consistent mem? */
     84 	/* Reserve */
     85 	for (addr = (unsigned long)dmah->vaddr, sz = size;
     86 	     sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
     87 		SetPageReserved(virt_to_page((void *)addr));
     88 	}
     89 
     90 	return dmah;
     91 }
     92 
     93 EXPORT_SYMBOL(drm_pci_alloc);
     94 
     95 /*
     96  * Free a PCI consistent memory block without freeing its descriptor.
     97  *
     98  * This function is for internal use in the Linux-specific DRM core code.
     99  */
    100 void __drm_legacy_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
    101 {
    102 	unsigned long addr;
    103 	size_t sz;
    104 
    105 	if (dmah->vaddr) {
    106 		/* XXX - Is virt_to_page() legal for consistent mem? */
    107 		/* Unreserve */
    108 		for (addr = (unsigned long)dmah->vaddr, sz = dmah->size;
    109 		     sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
    110 			ClearPageReserved(virt_to_page((void *)addr));
    111 		}
    112 		dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr,
    113 				  dmah->busaddr);
    114 	}
    115 }
    116 
    117 /**
    118  * drm_pci_free - Free a PCI consistent memory block
    119  * @dev: DRM device
    120  * @dmah: handle to memory block
    121  *
    122  * FIXME: This is a needless abstraction of the Linux dma-api and should be
    123  * removed.
    124  */
    125 void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
    126 {
    127 	__drm_legacy_pci_free(dev, dmah);
    128 	kfree(dmah);
    129 }
    130 
    131 EXPORT_SYMBOL(drm_pci_free);
    132 
    133 static int drm_get_pci_domain(struct drm_device *dev)
    134 {
    135 #ifndef __alpha__
    136 	/* For historical reasons, drm_get_pci_domain() is busticated
    137 	 * on most archs and has to remain so for userspace interface
    138 	 * < 1.4, except on alpha which was right from the beginning
    139 	 */
    140 	if (dev->if_version < 0x10004)
    141 		return 0;
    142 #endif /* __alpha__ */
    143 
    144 	return pci_domain_nr(dev->pdev->bus);
    145 }
    146 
    147 int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
    148 {
    149 	master->unique = kasprintf(GFP_KERNEL, "pci:%04x:%02x:%02x.%d",
    150 					drm_get_pci_domain(dev),
    151 					dev->pdev->bus->number,
    152 					PCI_SLOT(dev->pdev->devfn),
    153 					PCI_FUNC(dev->pdev->devfn));
    154 	if (!master->unique)
    155 		return -ENOMEM;
    156 
    157 	master->unique_len = strlen(master->unique);
    158 	return 0;
    159 }
    160 
    161 static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
    162 {
    163 	if ((p->busnum >> 8) != drm_get_pci_domain(dev) ||
    164 	    (p->busnum & 0xff) != dev->pdev->bus->number ||
    165 	    p->devnum != PCI_SLOT(dev->pdev->devfn) || p->funcnum != PCI_FUNC(dev->pdev->devfn))
    166 		return -EINVAL;
    167 
    168 	p->irq = dev->pdev->irq;
    169 
    170 	DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum,
    171 		  p->irq);
    172 	return 0;
    173 }
    174 
    175 /**
    176  * drm_irq_by_busid - Get interrupt from bus ID
    177  * @dev: DRM device
    178  * @data: IOCTL parameter pointing to a drm_irq_busid structure
    179  * @file_priv: DRM file private.
    180  *
    181  * Finds the PCI device with the specified bus id and gets its IRQ number.
    182  * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
    183  * to that of the device that this DRM instance attached to.
    184  *
    185  * Return: 0 on success or a negative error code on failure.
    186  */
    187 int drm_irq_by_busid(struct drm_device *dev, void *data,
    188 		     struct drm_file *file_priv)
    189 {
    190 	struct drm_irq_busid *p = data;
    191 
    192 	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
    193 		return -EOPNOTSUPP;
    194 
    195 	/* UMS was only ever support on PCI devices. */
    196 	if (WARN_ON(!dev->pdev))
    197 		return -EINVAL;
    198 
    199 	if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
    200 		return -EOPNOTSUPP;
    201 
    202 	return drm_pci_irq_by_busid(dev, p);
    203 }
    204 
    205 static void drm_pci_agp_init(struct drm_device *dev)
    206 {
    207 	if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
    208 		if (pci_find_capability(dev->pdev, PCI_CAP_ID_AGP))
    209 			dev->agp = drm_agp_init(dev);
    210 		if (dev->agp) {
    211 			dev->agp->agp_mtrr = arch_phys_wc_add(
    212 				dev->agp->agp_info.aper_base,
    213 				dev->agp->agp_info.aper_size *
    214 				1024 * 1024);
    215 		}
    216 	}
    217 }
    218 
    219 void drm_pci_agp_destroy(struct drm_device *dev)
    220 {
    221 	if (dev->agp) {
    222 		arch_phys_wc_del(dev->agp->agp_mtrr);
    223 		drm_legacy_agp_clear(dev);
    224 		kfree(dev->agp);
    225 		dev->agp = NULL;
    226 	}
    227 }
    228 
    229 /**
    230  * drm_get_pci_dev - Register a PCI device with the DRM subsystem
    231  * @pdev: PCI device
    232  * @ent: entry from the PCI ID table that matches @pdev
    233  * @driver: DRM device driver
    234  *
    235  * Attempt to gets inter module "drm" information. If we are first
    236  * then register the character device and inter module information.
    237  * Try and register, if we fail to register, backout previous work.
    238  *
    239  * NOTE: This function is deprecated, please use drm_dev_alloc() and
    240  * drm_dev_register() instead and remove your &drm_driver.load callback.
    241  *
    242  * Return: 0 on success or a negative error code on failure.
    243  */
    244 int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
    245 		    struct drm_driver *driver)
    246 {
    247 	struct drm_device *dev;
    248 	int ret;
    249 
    250 	DRM_DEBUG("\n");
    251 
    252 	dev = drm_dev_alloc(driver, &pdev->dev);
    253 	if (IS_ERR(dev))
    254 		return PTR_ERR(dev);
    255 
    256 	ret = pci_enable_device(pdev);
    257 	if (ret)
    258 		goto err_free;
    259 
    260 	dev->pdev = pdev;
    261 #ifdef __alpha__
    262 	dev->hose = pdev->sysdata;
    263 #endif
    264 
    265 	if (drm_core_check_feature(dev, DRIVER_MODESET))
    266 		pci_set_drvdata(pdev, dev);
    267 
    268 	drm_pci_agp_init(dev);
    269 
    270 	ret = drm_dev_register(dev, ent->driver_data);
    271 	if (ret)
    272 		goto err_agp;
    273 
    274 	/* No locking needed since shadow-attach is single-threaded since it may
    275 	 * only be called from the per-driver module init hook. */
    276 	if (drm_core_check_feature(dev, DRIVER_LEGACY))
    277 		list_add_tail(&dev->legacy_dev_list, &driver->legacy_dev_list);
    278 
    279 	return 0;
    280 
    281 err_agp:
    282 	drm_pci_agp_destroy(dev);
    283 	pci_disable_device(pdev);
    284 err_free:
    285 	drm_dev_put(dev);
    286 	return ret;
    287 }
    288 EXPORT_SYMBOL(drm_get_pci_dev);
    289 
    290 #ifdef CONFIG_DRM_LEGACY
    291 
    292 /**
    293  * drm_legacy_pci_init - shadow-attach a legacy DRM PCI driver
    294  * @driver: DRM device driver
    295  * @pdriver: PCI device driver
    296  *
    297  * This is only used by legacy dri1 drivers and deprecated.
    298  *
    299  * Return: 0 on success or a negative error code on failure.
    300  */
    301 int drm_legacy_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
    302 {
    303 	struct pci_dev *pdev = NULL;
    304 	const struct pci_device_id *pid;
    305 	int i;
    306 
    307 	DRM_DEBUG("\n");
    308 
    309 	if (WARN_ON(!(driver->driver_features & DRIVER_LEGACY)))
    310 		return -EINVAL;
    311 
    312 	/* If not using KMS, fall back to stealth mode manual scanning. */
    313 	INIT_LIST_HEAD(&driver->legacy_dev_list);
    314 	for (i = 0; pdriver->id_table[i].vendor != 0; i++) {
    315 		pid = &pdriver->id_table[i];
    316 
    317 		/* Loop around setting up a DRM device for each PCI device
    318 		 * matching our ID and device class.  If we had the internal
    319 		 * function that pci_get_subsys and pci_get_class used, we'd
    320 		 * be able to just pass pid in instead of doing a two-stage
    321 		 * thing.
    322 		 */
    323 		pdev = NULL;
    324 		while ((pdev =
    325 			pci_get_subsys(pid->vendor, pid->device, pid->subvendor,
    326 				       pid->subdevice, pdev)) != NULL) {
    327 			if ((pdev->class & pid->class_mask) != pid->class)
    328 				continue;
    329 
    330 			/* stealth mode requires a manual probe */
    331 			pci_dev_get(pdev);
    332 			drm_get_pci_dev(pdev, pid, driver);
    333 		}
    334 	}
    335 	return 0;
    336 }
    337 EXPORT_SYMBOL(drm_legacy_pci_init);
    338 
    339 /**
    340  * drm_legacy_pci_exit - unregister shadow-attach legacy DRM driver
    341  * @driver: DRM device driver
    342  * @pdriver: PCI device driver
    343  *
    344  * Unregister a DRM driver shadow-attached through drm_legacy_pci_init(). This
    345  * is deprecated and only used by dri1 drivers.
    346  */
    347 void drm_legacy_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
    348 {
    349 	struct drm_device *dev, *tmp;
    350 	DRM_DEBUG("\n");
    351 
    352 	if (!(driver->driver_features & DRIVER_LEGACY)) {
    353 		WARN_ON(1);
    354 	} else {
    355 		list_for_each_entry_safe(dev, tmp, &driver->legacy_dev_list,
    356 					 legacy_dev_list) {
    357 			list_del(&dev->legacy_dev_list);
    358 			drm_put_dev(dev);
    359 		}
    360 	}
    361 	DRM_INFO("Module unloaded\n");
    362 }
    363 EXPORT_SYMBOL(drm_legacy_pci_exit);
    364 
    365 #endif
    366