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