drm_pci.c revision 1.24 1 /* $NetBSD: drm_pci.c,v 1.24 2018/08/27 07:55:59 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: drm_pci.c,v 1.24 2018/08/27 07:55:59 riastradh Exp $");
34
35 #include <sys/types.h>
36 #include <sys/errno.h>
37 #include <sys/systm.h>
38
39 #include <dev/pci/pcivar.h>
40
41 #include <drm/drmP.h>
42 #include <drm/drm_legacy.h>
43 #include <drm/drm_internal.h>
44
45 struct drm_bus_irq_cookie {
46 pci_intr_handle_t *intr_handles;
47 void *ih_cookie;
48 };
49
50 static const struct pci_attach_args *
51 drm_pci_attach_args(struct drm_device *dev)
52 {
53 return &dev->pdev->pd_pa;
54 }
55
56 int
57 drm_pci_init(struct drm_driver *driver __unused,
58 struct pci_driver *pdriver __unused)
59 {
60
61 return 0;
62 }
63
64 void
65 drm_pci_exit(struct drm_driver *driver __unused,
66 struct pci_driver *pdriver __unused)
67 {
68 }
69
70 int
71 drm_pci_attach(device_t self, const struct pci_attach_args *pa,
72 struct pci_dev *pdev, struct drm_driver *driver, unsigned long cookie,
73 struct drm_device **devp)
74 {
75 struct drm_device *dev;
76 unsigned int unit;
77 int ret;
78
79 /* Ensure the drm agp hooks are installed. */
80 /* XXX errno NetBSD->Linux */
81 ret = -drmkms_pci_agp_guarantee_initialized();
82 if (ret)
83 goto fail0;
84
85 /* Initialize the Linux PCI device descriptor. */
86 linux_pci_dev_init(pdev, self, pa, 0);
87
88 /* Create a DRM device. */
89 dev = drm_dev_alloc(driver, self);
90 if (dev == NULL) {
91 ret = -ENOMEM;
92 goto fail0;
93 }
94
95 dev->pdev = pdev;
96 pdev->pd_drm_dev = dev; /* XXX Nouveau kludge. */
97
98 /* XXX Set the power state to D0? */
99
100 /* Set up the bus space and bus DMA tags. */
101 dev->bst = pa->pa_memt;
102 /* XXX Let the driver say something about 32-bit vs 64-bit DMA? */
103 dev->bus_dmat = (pci_dma64_available(pa)? pa->pa_dmat64 : pa->pa_dmat);
104 dev->dmat = dev->bus_dmat;
105 dev->dmat_subregion_p = false;
106
107 /* Find all the memory maps. */
108 CTASSERT(PCI_NUM_RESOURCES < (SIZE_MAX / sizeof(dev->bus_maps[0])));
109 dev->bus_maps = kmem_zalloc(PCI_NUM_RESOURCES *
110 sizeof(dev->bus_maps[0]), KM_SLEEP);
111 dev->bus_nmaps = PCI_NUM_RESOURCES;
112 for (unit = 0; unit < PCI_NUM_RESOURCES; unit++) {
113 struct drm_bus_map *const bm = &dev->bus_maps[unit];
114 const int reg = PCI_BAR(unit);
115 const pcireg_t type =
116 pci_mapreg_type(pa->pa_pc, pa->pa_tag, reg);
117
118 /* Reject non-memory mappings. */
119 if ((type & PCI_MAPREG_TYPE_MEM) != PCI_MAPREG_TYPE_MEM) {
120 aprint_debug_dev(self, "map %u has non-memory type:"
121 " 0x%"PRIxMAX"\n", unit, (uintmax_t)type);
122 continue;
123 }
124
125 /* Inquire about it. We'll map it in drm_legacy_ioremap. */
126 if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, reg, type,
127 &bm->bm_base, &bm->bm_size, &bm->bm_flags) != 0) {
128 aprint_debug_dev(self, "map %u failed\n", unit);
129 continue;
130 }
131
132 /* Assume since it is a memory mapping it can be linear. */
133 bm->bm_flags |= BUS_SPACE_MAP_LINEAR;
134 }
135
136 /* Set up AGP stuff if requested. */
137 if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
138 if (drm_pci_device_is_agp(dev))
139 dev->agp = drm_agp_init(dev);
140 if (dev->agp)
141 dev->agp->agp_mtrr = arch_phys_wc_add(dev->agp->base,
142 dev->agp->agp_info.aki_info.ai_aperture_size);
143 }
144
145 /* Register the DRM device and do driver-specific initialization. */
146 ret = drm_dev_register(dev, cookie);
147 if (ret)
148 goto fail1;
149
150 /* Success! */
151 *devp = dev;
152 return 0;
153
154 fail2: __unused
155 drm_dev_unregister(dev);
156 fail1: drm_pci_agp_destroy(dev);
157 dev->bus_nmaps = 0;
158 kmem_free(dev->bus_maps, PCI_NUM_RESOURCES * sizeof(dev->bus_maps[0]));
159 if (dev->dmat_subregion_p)
160 bus_dmatag_destroy(dev->dmat);
161 drm_dev_unref(dev);
162 fail0: return ret;
163 }
164
165 int
166 drm_pci_detach(struct drm_device *dev, int flags __unused)
167 {
168
169 /* Do driver-specific detachment and unregister the device. */
170 drm_dev_unregister(dev);
171
172 /* Tear down AGP stuff if necessary. */
173 drm_pci_agp_destroy(dev);
174
175 /* Free the record of available bus space mappings. */
176 dev->bus_nmaps = 0;
177 kmem_free(dev->bus_maps, PCI_NUM_RESOURCES * sizeof(dev->bus_maps[0]));
178
179 /* Tear down bus space and bus DMA tags. */
180 if (dev->dmat_subregion_p)
181 bus_dmatag_destroy(dev->dmat);
182
183 drm_dev_unref(dev);
184
185 return 0;
186 }
187
188 void
189 drm_pci_agp_destroy(struct drm_device *dev)
190 {
191
192 if (dev->agp) {
193 arch_phys_wc_del(dev->agp->agp_mtrr);
194 drm_agp_clear(dev);
195 kfree(dev->agp); /* XXX Should go in drm_agp_clear... */
196 dev->agp = NULL;
197 }
198 }
199
200 int
201 drm_pci_request_irq(struct drm_device *dev, int flags)
202 {
203 const char *const name = device_xname(dev->dev);
204 int (*const handler)(void *) = dev->driver->irq_handler;
205 const struct pci_attach_args *const pa = drm_pci_attach_args(dev);
206 const char *intrstr;
207 char intrbuf[PCI_INTRSTR_LEN];
208 struct drm_bus_irq_cookie *irq_cookie;
209
210 irq_cookie = kmem_alloc(sizeof(*irq_cookie), KM_SLEEP);
211
212 if (dev->pdev->msi_enabled) {
213 if (dev->pdev->pd_intr_handles == NULL) {
214 if (pci_msi_alloc_exact(pa, &irq_cookie->intr_handles,
215 1)) {
216 aprint_error_dev(dev->dev,
217 "couldn't allocate MSI (%s)\n", name);
218 goto error;
219 }
220 } else {
221 irq_cookie->intr_handles = dev->pdev->pd_intr_handles;
222 dev->pdev->pd_intr_handles = NULL;
223 }
224 } else {
225 if (pci_intx_alloc(pa, &irq_cookie->intr_handles)) {
226 aprint_error_dev(dev->dev,
227 "couldn't allocate INTx interrupt (%s)\n", name);
228 goto error;
229 }
230 }
231
232 intrstr = pci_intr_string(pa->pa_pc, irq_cookie->intr_handles[0],
233 intrbuf, sizeof(intrbuf));
234 irq_cookie->ih_cookie = pci_intr_establish_xname(pa->pa_pc,
235 irq_cookie->intr_handles[0], IPL_DRM, handler, dev, name);
236 if (irq_cookie->ih_cookie == NULL) {
237 aprint_error_dev(dev->dev,
238 "couldn't establish interrupt at %s (%s)\n", intrstr, name);
239 pci_intr_release(pa->pa_pc, irq_cookie->intr_handles, 1);
240 goto error;
241 }
242
243 aprint_normal_dev(dev->dev, "interrupting at %s (%s)\n", intrstr, name);
244 dev->irq_cookie = irq_cookie;
245 return 0;
246
247 error:
248 kmem_free(irq_cookie, sizeof(*irq_cookie));
249 return -ENOENT;
250 }
251
252 void
253 drm_pci_free_irq(struct drm_device *dev)
254 {
255 struct drm_bus_irq_cookie *const cookie = dev->irq_cookie;
256 const struct pci_attach_args *pa = drm_pci_attach_args(dev);
257
258 pci_intr_disestablish(pa->pa_pc, cookie->ih_cookie);
259 pci_intr_release(pa->pa_pc, cookie->intr_handles, 1);
260 kmem_free(cookie, sizeof(*cookie));
261 dev->irq_cookie = NULL;
262 }
263
264 int
265 drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
266 {
267 const struct pci_attach_args *const pa = &dev->pdev->pd_pa;
268
269 master->unique = kasprintf(GFP_KERNEL, "pci:%04x:%02x:%02x.%d",
270 device_unit(device_parent(dev->dev)),
271 pa->pa_bus, pa->pa_device, pa->pa_function);
272 if (master->unique == NULL)
273 return -ENOMEM;
274 master->unique_len = strlen(master->unique);
275
276 return 0;
277 }
278
279 int
280 drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file)
281 {
282
283 return -ENODEV;
284 }
285
286 int
287 drm_pci_set_unique(struct drm_device *dev, struct drm_master *master,
288 struct drm_unique *unique)
289 {
290 char kbuf[64], ubuf[64];
291 int ret;
292
293 /* Reject excessively long unique strings. */
294 if (unique->unique_len > sizeof(ubuf) - 1)
295 return -EINVAL;
296
297 /* Copy in the alleged unique string, NUL-terminated. */
298 ret = -copyin(unique->unique, ubuf, unique->unique_len);
299 if (ret)
300 return ret;
301 ubuf[unique->unique_len] = '\0';
302
303 /* Make sure it matches what we expect. */
304 snprintf(kbuf, sizeof kbuf, "PCI:%d:%ld:%ld", dev->pdev->bus->number,
305 PCI_SLOT(dev->pdev->devfn), PCI_FUNC(dev->pdev->devfn));
306 if (strncmp(kbuf, ubuf, sizeof(kbuf)) != 0)
307 return -EINVAL;
308
309 /* Remember it. */
310 master->unique = kstrdup(ubuf, GFP_KERNEL);
311 master->unique_len = strlen(master->unique);
312
313 /* Success! */
314 return 0;
315 }
316