xref: /openbmc/linux/drivers/gpu/drm/drm_pci.c (revision 644adc3d)
1c0e09200SDave Airlie /*
2c0e09200SDave Airlie  * Copyright 2003 José Fonseca.
3c0e09200SDave Airlie  * Copyright 2003 Leif Delgass.
4c0e09200SDave Airlie  * All Rights Reserved.
5c0e09200SDave Airlie  *
6c0e09200SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
7c0e09200SDave Airlie  * copy of this software and associated documentation files (the "Software"),
8c0e09200SDave Airlie  * to deal in the Software without restriction, including without limitation
9c0e09200SDave Airlie  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10c0e09200SDave Airlie  * and/or sell copies of the Software, and to permit persons to whom the
11c0e09200SDave Airlie  * Software is furnished to do so, subject to the following conditions:
12c0e09200SDave Airlie  *
13c0e09200SDave Airlie  * The above copyright notice and this permission notice (including the next
14c0e09200SDave Airlie  * paragraph) shall be included in all copies or substantial portions of the
15c0e09200SDave Airlie  * Software.
16c0e09200SDave Airlie  *
17c0e09200SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18c0e09200SDave Airlie  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19c0e09200SDave Airlie  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20c0e09200SDave Airlie  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21c0e09200SDave Airlie  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22c0e09200SDave Airlie  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23c0e09200SDave Airlie  */
24c0e09200SDave Airlie 
25c0e09200SDave Airlie #include <linux/dma-mapping.h>
262d1a8a48SPaul Gortmaker #include <linux/export.h>
278dbe1b4aSLaurent Pinchart #include <linux/list.h>
288dbe1b4aSLaurent Pinchart #include <linux/mutex.h>
290500c04eSSam Ravnborg #include <linux/pci.h>
300500c04eSSam Ravnborg #include <linux/slab.h>
310500c04eSSam Ravnborg 
320500c04eSSam Ravnborg #include <drm/drm.h>
330500c04eSSam Ravnborg #include <drm/drm_drv.h>
340500c04eSSam Ravnborg #include <drm/drm_print.h>
350500c04eSSam Ravnborg 
3643fc884eSVille Syrjälä #include "drm_internal.h"
37ba8286faSDaniel Vetter #include "drm_legacy.h"
38c0e09200SDave Airlie 
3933775336SDaniel Vetter #ifdef CONFIG_DRM_LEGACY
408dbe1b4aSLaurent Pinchart /* List of devices hanging off drivers with stealth attach. */
418dbe1b4aSLaurent Pinchart static LIST_HEAD(legacy_dev_list);
428dbe1b4aSLaurent Pinchart static DEFINE_MUTEX(legacy_dev_list_lock);
4333775336SDaniel Vetter #endif
44c0e09200SDave Airlie 
drm_get_pci_domain(struct drm_device * dev)458410ea3bSDave Airlie static int drm_get_pci_domain(struct drm_device *dev)
468410ea3bSDave Airlie {
478410ea3bSDave Airlie #ifndef __alpha__
488410ea3bSDave Airlie 	/* For historical reasons, drm_get_pci_domain() is busticated
498410ea3bSDave Airlie 	 * on most archs and has to remain so for userspace interface
508410ea3bSDave Airlie 	 * < 1.4, except on alpha which was right from the beginning
518410ea3bSDave Airlie 	 */
528410ea3bSDave Airlie 	if (dev->if_version < 0x10004)
538410ea3bSDave Airlie 		return 0;
548410ea3bSDave Airlie #endif /* __alpha__ */
558410ea3bSDave Airlie 
5636b73b05SThomas Zimmermann 	return pci_domain_nr(to_pci_dev(dev->dev)->bus);
578410ea3bSDave Airlie }
588410ea3bSDave Airlie 
drm_pci_set_busid(struct drm_device * dev,struct drm_master * master)59915b4d11SDavid Herrmann int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
608410ea3bSDave Airlie {
6136b73b05SThomas Zimmermann 	struct pci_dev *pdev = to_pci_dev(dev->dev);
6236b73b05SThomas Zimmermann 
63d0a39164SDavid Herrmann 	master->unique = kasprintf(GFP_KERNEL, "pci:%04x:%02x:%02x.%d",
648410ea3bSDave Airlie 					drm_get_pci_domain(dev),
6536b73b05SThomas Zimmermann 					pdev->bus->number,
6636b73b05SThomas Zimmermann 					PCI_SLOT(pdev->devfn),
6736b73b05SThomas Zimmermann 					PCI_FUNC(pdev->devfn));
68d0a39164SDavid Herrmann 	if (!master->unique)
69d0a39164SDavid Herrmann 		return -ENOMEM;
708410ea3bSDave Airlie 
71d0a39164SDavid Herrmann 	master->unique_len = strlen(master->unique);
728410ea3bSDave Airlie 	return 0;
738410ea3bSDave Airlie }
748410ea3bSDave Airlie 
75*644adc3dSThomas Zimmermann #ifdef CONFIG_DRM_LEGACY
76*644adc3dSThomas Zimmermann 
drm_legacy_pci_irq_by_busid(struct drm_device * dev,struct drm_irq_busid * p)77*644adc3dSThomas Zimmermann static int drm_legacy_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
788410ea3bSDave Airlie {
7936b73b05SThomas Zimmermann 	struct pci_dev *pdev = to_pci_dev(dev->dev);
8036b73b05SThomas Zimmermann 
818410ea3bSDave Airlie 	if ((p->busnum >> 8) != drm_get_pci_domain(dev) ||
8236b73b05SThomas Zimmermann 	    (p->busnum & 0xff) != pdev->bus->number ||
8336b73b05SThomas Zimmermann 	    p->devnum != PCI_SLOT(pdev->devfn) || p->funcnum != PCI_FUNC(pdev->devfn))
848410ea3bSDave Airlie 		return -EINVAL;
858410ea3bSDave Airlie 
8636b73b05SThomas Zimmermann 	p->irq = pdev->irq;
878410ea3bSDave Airlie 
888410ea3bSDave Airlie 	DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum,
898410ea3bSDave Airlie 		  p->irq);
908410ea3bSDave Airlie 	return 0;
918410ea3bSDave Airlie }
928410ea3bSDave Airlie 
93eaaf8f0fSDaniel Vetter /**
948df4ec51SDaniel Vetter  * drm_legacy_irq_by_busid - Get interrupt from bus ID
95c6a1af8aSThierry Reding  * @dev: DRM device
96c6a1af8aSThierry Reding  * @data: IOCTL parameter pointing to a drm_irq_busid structure
97c6a1af8aSThierry Reding  * @file_priv: DRM file private.
98eaaf8f0fSDaniel Vetter  *
99eaaf8f0fSDaniel Vetter  * Finds the PCI device with the specified bus id and gets its IRQ number.
100eaaf8f0fSDaniel Vetter  * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
101eaaf8f0fSDaniel Vetter  * to that of the device that this DRM instance attached to.
102c6a1af8aSThierry Reding  *
103c6a1af8aSThierry Reding  * Return: 0 on success or a negative error code on failure.
104eaaf8f0fSDaniel Vetter  */
drm_legacy_irq_by_busid(struct drm_device * dev,void * data,struct drm_file * file_priv)1058df4ec51SDaniel Vetter int drm_legacy_irq_by_busid(struct drm_device *dev, void *data,
106eaaf8f0fSDaniel Vetter 			    struct drm_file *file_priv)
107eaaf8f0fSDaniel Vetter {
108eaaf8f0fSDaniel Vetter 	struct drm_irq_busid *p = data;
109eaaf8f0fSDaniel Vetter 
110fa538645SDaniel Vetter 	if (!drm_core_check_feature(dev, DRIVER_LEGACY))
11169fdf420SChris Wilson 		return -EOPNOTSUPP;
112eaaf8f0fSDaniel Vetter 
113eaaf8f0fSDaniel Vetter 	/* UMS was only ever support on PCI devices. */
11436b73b05SThomas Zimmermann 	if (WARN_ON(!dev_is_pci(dev->dev)))
115eaaf8f0fSDaniel Vetter 		return -EINVAL;
116eaaf8f0fSDaniel Vetter 
117eaaf8f0fSDaniel Vetter 	if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
11869fdf420SChris Wilson 		return -EOPNOTSUPP;
119eaaf8f0fSDaniel Vetter 
120*644adc3dSThomas Zimmermann 	return drm_legacy_pci_irq_by_busid(dev, p);
121eaaf8f0fSDaniel Vetter }
122eaaf8f0fSDaniel Vetter 
drm_legacy_pci_agp_destroy(struct drm_device * dev)1236bff2279SThomas Zimmermann void drm_legacy_pci_agp_destroy(struct drm_device *dev)
124ee21ec77SChris Wilson {
125ee21ec77SChris Wilson 	if (dev->agp) {
126ee21ec77SChris Wilson 		arch_phys_wc_del(dev->agp->agp_mtrr);
127ee21ec77SChris Wilson 		drm_legacy_agp_clear(dev);
128ee21ec77SChris Wilson 		kfree(dev->agp);
129ee21ec77SChris Wilson 		dev->agp = NULL;
130ee21ec77SChris Wilson 	}
131ee21ec77SChris Wilson }
132ee21ec77SChris Wilson 
drm_legacy_pci_agp_init(struct drm_device * dev)1336bff2279SThomas Zimmermann static void drm_legacy_pci_agp_init(struct drm_device *dev)
1348410ea3bSDave Airlie {
135d9906753SDaniel Vetter 	if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
13636b73b05SThomas Zimmermann 		if (pci_find_capability(to_pci_dev(dev->dev), PCI_CAP_ID_AGP))
13704dfe19aSThomas Zimmermann 			dev->agp = drm_legacy_agp_init(dev);
13828185647SDaniel Vetter 		if (dev->agp) {
139f435046dSAndy Lutomirski 			dev->agp->agp_mtrr = arch_phys_wc_add(
140f435046dSAndy Lutomirski 				dev->agp->agp_info.aper_base,
1418410ea3bSDave Airlie 				dev->agp->agp_info.aper_size *
142f435046dSAndy Lutomirski 				1024 * 1024);
1438410ea3bSDave Airlie 		}
1448410ea3bSDave Airlie 	}
1458410ea3bSDave Airlie }
1468410ea3bSDave Airlie 
drm_legacy_get_pci_dev(struct pci_dev * pdev,const struct pci_device_id * ent,const struct drm_driver * driver)1476bff2279SThomas Zimmermann static int drm_legacy_get_pci_dev(struct pci_dev *pdev,
148c393fbaeSDaniel Vetter 				  const struct pci_device_id *ent,
149b1dda997SLaurent Pinchart 				  const struct drm_driver *driver)
150dcdb1674SJordan Crouse {
151dcdb1674SJordan Crouse 	struct drm_device *dev;
152dcdb1674SJordan Crouse 	int ret;
153dcdb1674SJordan Crouse 
154dcdb1674SJordan Crouse 	DRM_DEBUG("\n");
155dcdb1674SJordan Crouse 
1561bb72532SDavid Herrmann 	dev = drm_dev_alloc(driver, &pdev->dev);
1570f288605STom Gundersen 	if (IS_ERR(dev))
1580f288605STom Gundersen 		return PTR_ERR(dev);
159dcdb1674SJordan Crouse 
160dcdb1674SJordan Crouse 	ret = pci_enable_device(pdev);
161dcdb1674SJordan Crouse 	if (ret)
162c22f0aceSDavid Herrmann 		goto err_free;
163dcdb1674SJordan Crouse 
164dcdb1674SJordan Crouse #ifdef __alpha__
165dcdb1674SJordan Crouse 	dev->hose = pdev->sysdata;
166dcdb1674SJordan Crouse #endif
167dcdb1674SJordan Crouse 
1686bff2279SThomas Zimmermann 	drm_legacy_pci_agp_init(dev);
1692c695fa0SDaniel Vetter 
170c22f0aceSDavid Herrmann 	ret = drm_dev_register(dev, ent->driver_data);
171dcdb1674SJordan Crouse 	if (ret)
1722c695fa0SDaniel Vetter 		goto err_agp;
173dcdb1674SJordan Crouse 
1748dbe1b4aSLaurent Pinchart 	if (drm_core_check_feature(dev, DRIVER_LEGACY)) {
1758dbe1b4aSLaurent Pinchart 		mutex_lock(&legacy_dev_list_lock);
1768dbe1b4aSLaurent Pinchart 		list_add_tail(&dev->legacy_dev_list, &legacy_dev_list);
1778dbe1b4aSLaurent Pinchart 		mutex_unlock(&legacy_dev_list_lock);
1788dbe1b4aSLaurent Pinchart 	}
179b3f2333dSDaniel Vetter 
180dcdb1674SJordan Crouse 	return 0;
181dcdb1674SJordan Crouse 
1822c695fa0SDaniel Vetter err_agp:
1836bff2279SThomas Zimmermann 	drm_legacy_pci_agp_destroy(dev);
184dcdb1674SJordan Crouse 	pci_disable_device(pdev);
185c22f0aceSDavid Herrmann err_free:
186ffeeeed0SAishwarya Pant 	drm_dev_put(dev);
187dcdb1674SJordan Crouse 	return ret;
188dcdb1674SJordan Crouse }
1891be9d5f0SThomas Zimmermann 
190dcdb1674SJordan Crouse /**
19110631d72SDaniel Vetter  * drm_legacy_pci_init - shadow-attach a legacy DRM PCI driver
192c6a1af8aSThierry Reding  * @driver: DRM device driver
193c6a1af8aSThierry Reding  * @pdriver: PCI device driver
194dcdb1674SJordan Crouse  *
19510631d72SDaniel Vetter  * This is only used by legacy dri1 drivers and deprecated.
1966e3f797cSDaniel Vetter  *
197c6a1af8aSThierry Reding  * Return: 0 on success or a negative error code on failure.
198dcdb1674SJordan Crouse  */
drm_legacy_pci_init(const struct drm_driver * driver,struct pci_driver * pdriver)199b1dda997SLaurent Pinchart int drm_legacy_pci_init(const struct drm_driver *driver,
200b1dda997SLaurent Pinchart 			struct pci_driver *pdriver)
201dcdb1674SJordan Crouse {
202dcdb1674SJordan Crouse 	struct pci_dev *pdev = NULL;
203dcdb1674SJordan Crouse 	const struct pci_device_id *pid;
204dcdb1674SJordan Crouse 	int i;
205dcdb1674SJordan Crouse 
2068410ea3bSDave Airlie 	DRM_DEBUG("\n");
2078410ea3bSDave Airlie 
20810631d72SDaniel Vetter 	if (WARN_ON(!(driver->driver_features & DRIVER_LEGACY)))
20910631d72SDaniel Vetter 		return -EINVAL;
210dcdb1674SJordan Crouse 
211dcdb1674SJordan Crouse 	/* If not using KMS, fall back to stealth mode manual scanning. */
2128410ea3bSDave Airlie 	for (i = 0; pdriver->id_table[i].vendor != 0; i++) {
2138410ea3bSDave Airlie 		pid = &pdriver->id_table[i];
214dcdb1674SJordan Crouse 
215dcdb1674SJordan Crouse 		/* Loop around setting up a DRM device for each PCI device
216dcdb1674SJordan Crouse 		 * matching our ID and device class.  If we had the internal
217dcdb1674SJordan Crouse 		 * function that pci_get_subsys and pci_get_class used, we'd
218dcdb1674SJordan Crouse 		 * be able to just pass pid in instead of doing a two-stage
219dcdb1674SJordan Crouse 		 * thing.
220dcdb1674SJordan Crouse 		 */
221dcdb1674SJordan Crouse 		pdev = NULL;
222dcdb1674SJordan Crouse 		while ((pdev =
223dcdb1674SJordan Crouse 			pci_get_subsys(pid->vendor, pid->device, pid->subvendor,
224dcdb1674SJordan Crouse 				       pid->subdevice, pdev)) != NULL) {
225dcdb1674SJordan Crouse 			if ((pdev->class & pid->class_mask) != pid->class)
226dcdb1674SJordan Crouse 				continue;
227dcdb1674SJordan Crouse 
228dcdb1674SJordan Crouse 			/* stealth mode requires a manual probe */
229dcdb1674SJordan Crouse 			pci_dev_get(pdev);
2306bff2279SThomas Zimmermann 			drm_legacy_get_pci_dev(pdev, pid, driver);
231dcdb1674SJordan Crouse 		}
232dcdb1674SJordan Crouse 	}
233dcdb1674SJordan Crouse 	return 0;
234dcdb1674SJordan Crouse }
23510631d72SDaniel Vetter EXPORT_SYMBOL(drm_legacy_pci_init);
236dcdb1674SJordan Crouse 
237c6a1af8aSThierry Reding /**
23810631d72SDaniel Vetter  * drm_legacy_pci_exit - unregister shadow-attach legacy DRM driver
239c6a1af8aSThierry Reding  * @driver: DRM device driver
240c6a1af8aSThierry Reding  * @pdriver: PCI device driver
241c6a1af8aSThierry Reding  *
24210631d72SDaniel Vetter  * Unregister a DRM driver shadow-attached through drm_legacy_pci_init(). This
24310631d72SDaniel Vetter  * is deprecated and only used by dri1 drivers.
244c6a1af8aSThierry Reding  */
drm_legacy_pci_exit(const struct drm_driver * driver,struct pci_driver * pdriver)245b1dda997SLaurent Pinchart void drm_legacy_pci_exit(const struct drm_driver *driver,
246b1dda997SLaurent Pinchart 			 struct pci_driver *pdriver)
24793711d8bSBjorn Helgaas {
24893711d8bSBjorn Helgaas 	struct drm_device *dev, *tmp;
249948de842SSuraj Upadhyay 
25093711d8bSBjorn Helgaas 	DRM_DEBUG("\n");
25193711d8bSBjorn Helgaas 
252fa538645SDaniel Vetter 	if (!(driver->driver_features & DRIVER_LEGACY)) {
25310631d72SDaniel Vetter 		WARN_ON(1);
25493711d8bSBjorn Helgaas 	} else {
2558dbe1b4aSLaurent Pinchart 		mutex_lock(&legacy_dev_list_lock);
2568dbe1b4aSLaurent Pinchart 		list_for_each_entry_safe(dev, tmp, &legacy_dev_list,
257b3f2333dSDaniel Vetter 					 legacy_dev_list) {
2588dbe1b4aSLaurent Pinchart 			if (dev->driver == driver) {
259b3f2333dSDaniel Vetter 				list_del(&dev->legacy_dev_list);
260c94adc4aSDaniel Vetter 				drm_put_dev(dev);
261b3f2333dSDaniel Vetter 			}
26293711d8bSBjorn Helgaas 		}
2638dbe1b4aSLaurent Pinchart 		mutex_unlock(&legacy_dev_list_lock);
2648dbe1b4aSLaurent Pinchart 	}
26593711d8bSBjorn Helgaas 	DRM_INFO("Module unloaded\n");
26693711d8bSBjorn Helgaas }
26710631d72SDaniel Vetter EXPORT_SYMBOL(drm_legacy_pci_exit);
2681be9d5f0SThomas Zimmermann 
2691be9d5f0SThomas Zimmermann #endif
270