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