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. */ |
41 | static LIST_HEAD(legacy_dev_list); |
42 | static DEFINE_MUTEX(legacy_dev_list_lock); |
43 | #endif |
44 | |
45 | static 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 | |
59 | int 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, fmt: "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 | |
77 | static 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 | */ |
105 | int 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, feature: 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, feature: DRIVER_HAVE_IRQ)) |
118 | return -EOPNOTSUPP; |
119 | |
120 | return drm_legacy_pci_irq_by_busid(dev, p); |
121 | } |
122 | |
123 | void drm_legacy_pci_agp_destroy(struct drm_device *dev) |
124 | { |
125 | if (dev->agp) { |
126 | arch_phys_wc_del(handle: dev->agp->agp_mtrr); |
127 | drm_legacy_agp_clear(dev); |
128 | kfree(objp: dev->agp); |
129 | dev->agp = NULL; |
130 | } |
131 | } |
132 | |
133 | static void drm_legacy_pci_agp_init(struct drm_device *dev) |
134 | { |
135 | if (drm_core_check_feature(dev, feature: 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 | base: dev->agp->agp_info.aper_base, |
141 | size: dev->agp->agp_info.aper_size * |
142 | 1024 * 1024); |
143 | } |
144 | } |
145 | } |
146 | |
147 | static 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, parent: &pdev->dev); |
157 | if (IS_ERR(ptr: dev)) |
158 | return PTR_ERR(ptr: dev); |
159 | |
160 | ret = pci_enable_device(dev: 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, flags: ent->driver_data); |
171 | if (ret) |
172 | goto err_agp; |
173 | |
174 | if (drm_core_check_feature(dev, feature: DRIVER_LEGACY)) { |
175 | mutex_lock(&legacy_dev_list_lock); |
176 | list_add_tail(new: &dev->legacy_dev_list, head: &legacy_dev_list); |
177 | mutex_unlock(lock: &legacy_dev_list_lock); |
178 | } |
179 | |
180 | return 0; |
181 | |
182 | err_agp: |
183 | drm_legacy_pci_agp_destroy(dev); |
184 | pci_disable_device(dev: pdev); |
185 | err_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 | */ |
199 | int 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(vendor: pid->vendor, device: pid->device, ss_vendor: pid->subvendor, |
224 | ss_device: pid->subdevice, from: 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(dev: pdev); |
230 | drm_legacy_get_pci_dev(pdev, ent: pid, driver); |
231 | } |
232 | } |
233 | return 0; |
234 | } |
235 | EXPORT_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 | */ |
245 | void 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(entry: &dev->legacy_dev_list); |
260 | drm_put_dev(dev); |
261 | } |
262 | } |
263 | mutex_unlock(lock: &legacy_dev_list_lock); |
264 | } |
265 | DRM_INFO("Module unloaded\n" ); |
266 | } |
267 | EXPORT_SYMBOL(drm_legacy_pci_exit); |
268 | |
269 | #endif |
270 | |