nouveau_pci.c revision 1.34 1 /* $NetBSD: nouveau_pci.c,v 1.34 2021/12/19 12:28:12 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2015 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: nouveau_pci.c,v 1.34 2021/12/19 12:28:12 riastradh Exp $");
34
35 #ifdef _KERNEL_OPT
36 #if defined(__arm__) || defined(__aarch64__)
37 #include "opt_fdt.h"
38 #endif
39 #endif
40
41 #include <sys/types.h>
42 #include <sys/atomic.h>
43 #include <sys/device.h>
44 #include <sys/queue.h>
45 #include <sys/workqueue.h>
46 #include <sys/module.h>
47
48 #ifdef FDT
49 #include <dev/fdt/fdtvar.h>
50 #endif
51
52 #include <drm/drm_pci.h>
53
54 #include <core/device.h>
55 #include <core/pci.h>
56
57 #include "nouveau_drv.h"
58 #include "nouveau_pci.h"
59
60 struct drm_device;
61
62 MODULE(MODULE_CLASS_DRIVER, nouveau_pci, "nouveau,drmkms_pci");
63
64 SIMPLEQ_HEAD(nouveau_pci_task_head, nouveau_pci_task);
65
66 struct nouveau_pci_softc {
67 device_t sc_dev;
68 struct pci_attach_args sc_pa;
69 struct lwp *sc_task_thread;
70 struct nouveau_pci_task_head sc_tasks;
71 struct workqueue *sc_task_wq;
72 struct drm_device *sc_drm_dev;
73 struct pci_dev sc_pci_dev;
74 struct nvkm_device *sc_nv_dev;
75 bool sc_pci_attached;
76 bool sc_nvdev_inited;
77 bool sc_dev_registered;
78 };
79
80 static int nouveau_pci_match(device_t, cfdata_t, void *);
81 static void nouveau_pci_attach(device_t, device_t, void *);
82 static void nouveau_pci_attach_real(device_t);
83 static int nouveau_pci_detach(device_t, int);
84
85 static bool nouveau_pci_suspend(device_t, const pmf_qual_t *);
86 static bool nouveau_pci_resume(device_t, const pmf_qual_t *);
87
88 static void nouveau_pci_task_work(struct work *, void *);
89
90 CFATTACH_DECL_NEW(nouveau_pci, sizeof(struct nouveau_pci_softc),
91 nouveau_pci_match, nouveau_pci_attach, nouveau_pci_detach, NULL);
92
93 /* Kludge to get this from nouveau_drm.c. */
94 extern struct drm_driver *const nouveau_drm_driver_pci;
95
96 static int
97 nouveau_pci_match(device_t parent, cfdata_t match, void *aux)
98 {
99 const struct pci_attach_args *const pa = aux;
100 struct pci_dev pdev;
101 struct nvkm_device *device;
102 int ret;
103
104 if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_NVIDIA &&
105 PCI_VENDOR(pa->pa_id) != PCI_VENDOR_NVIDIA_SGS)
106 return 0;
107
108 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY)
109 return 0;
110
111 /*
112 * NetBSD drm2 doesn't support Pascal, Volta or Turing based cards:
113 * 0x1580-0x15ff GP100
114 * 0x1b00-0x1b7f GP102
115 * 0x1b80-0x1bff GP104
116 * 0x1c00-0x1c7f GP106
117 * 0x1c80-0x1cff GP107
118 * 0x1d00-0x1d7f GP108
119 * 0x1d80-0x1dff GV100
120 * 0x1e00-0x1e7f TU102
121 * 0x1e80-0x1eff TU104
122 * 0x1f00-0x1f7f TU106
123 * 0x1f80-0x1fff TU117
124 * 0x2180-0x21ff TU116
125 *
126 * reduce this to >= 1580, so that new chipsets not explictly
127 * listed above will be picked up.
128 *
129 * XXX perhaps switch this to explicitly match known list.
130 */
131 if (PCI_PRODUCT(pa->pa_id) >= 0x1580)
132 return 0;
133
134 linux_pci_dev_init(&pdev, parent /* XXX bogus */, parent, pa, 0);
135 ret = nvkm_device_pci_new(&pdev, NULL, "error",
136 /* detect */ true, /* mmio */ false, /* subdev_mask */ 0, &device);
137 if (ret == 0) /* don't want to hang onto it */
138 nvkm_device_del(&device);
139 linux_pci_dev_destroy(&pdev);
140 if (ret) /* failure */
141 return 0;
142
143 return 6; /* XXX Beat genfb_pci... */
144 }
145
146 extern char *nouveau_config;
147 extern char *nouveau_debug;
148
149 static void
150 nouveau_pci_attach(device_t parent, device_t self, void *aux)
151 {
152 struct nouveau_pci_softc *const sc = device_private(self);
153 const struct pci_attach_args *const pa = aux;
154 int error;
155
156 pci_aprint_devinfo(pa, NULL);
157
158 /* Initialize the Linux PCI device descriptor. */
159 linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0);
160
161 sc->sc_dev = self;
162 sc->sc_pa = *pa;
163 sc->sc_task_thread = NULL;
164 SIMPLEQ_INIT(&sc->sc_tasks);
165 error = workqueue_create(&sc->sc_task_wq, "nouveau_pci",
166 &nouveau_pci_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE);
167 if (error) {
168 aprint_error_dev(self, "unable to create workqueue: %d\n",
169 error);
170 sc->sc_task_wq = NULL;
171 return;
172 }
173
174 #ifdef FDT
175 /*
176 * XXX Remove the simple framebuffer, assuming that this device
177 * will take over.
178 */
179 const char *fb_compatible[] = { "simple-framebuffer", NULL };
180 fdt_remove_bycompat(fb_compatible);
181 #endif
182
183 /*
184 * Defer the remainder of initialization until we have mounted
185 * the root file system and can load firmware images.
186 */
187 config_mountroot(self, &nouveau_pci_attach_real);
188 }
189
190 static void
191 nouveau_pci_attach_real(device_t self)
192 {
193 struct nouveau_pci_softc *const sc = device_private(self);
194 int error;
195
196 /*
197 * Cause any tasks issued synchronously during attach to be
198 * processed at the end of this function.
199 */
200 sc->sc_task_thread = curlwp;
201
202 /* XXX errno Linux->NetBSD */
203 error = -nvkm_device_pci_new(&sc->sc_pci_dev,
204 nouveau_config, nouveau_debug,
205 /* detect */ true, /* mmio */ true, /* subdev_mask */ ~0ULL,
206 &sc->sc_nv_dev);
207 if (error) {
208 aprint_error_dev(self, "unable to create nouveau device: %d\n",
209 error);
210 sc->sc_nv_dev = NULL;
211 goto out;
212 }
213
214 sc->sc_drm_dev = drm_dev_alloc(nouveau_drm_driver_pci, self);
215 if (IS_ERR(sc->sc_drm_dev)) {
216 aprint_error_dev(self, "unable to create drm device: %ld\n",
217 PTR_ERR(sc->sc_drm_dev));
218 sc->sc_drm_dev = NULL;
219 goto out;
220 }
221
222 /* XXX errno Linux->NetBSD */
223 error = -drm_pci_attach(sc->sc_drm_dev, &sc->sc_pci_dev);
224 if (error) {
225 aprint_error_dev(self, "unable to attach drm: %d\n", error);
226 goto out;
227 }
228 sc->sc_pci_attached = true;
229
230 /* XXX errno Linux->NetBSD */
231 error = -nouveau_drm_device_init(sc->sc_drm_dev);
232 if (error) {
233 aprint_error_dev(self, "unable to init nouveau: %d\n", error);
234 goto out;
235 }
236 sc->sc_nvdev_inited = true;
237
238 /* XXX errno Linux->NetBSD */
239 error = -drm_dev_register(sc->sc_drm_dev, 0);
240 if (error) {
241 aprint_error_dev(self, "unable to register drm: %d\n", error);
242 goto out;
243 }
244 sc->sc_dev_registered = true;
245
246 if (!pmf_device_register(self, &nouveau_pci_suspend,
247 &nouveau_pci_resume))
248 aprint_error_dev(self, "unable to establish power handler\n");
249
250 while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) {
251 struct nouveau_pci_task *const task =
252 SIMPLEQ_FIRST(&sc->sc_tasks);
253
254 SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, nt_u.queue);
255 (*task->nt_fn)(task);
256 }
257
258 out: /* Cause any subesquent tasks to be processed by the workqueue. */
259 atomic_store_relaxed(&sc->sc_task_thread, NULL);
260 }
261
262 static int
263 nouveau_pci_detach(device_t self, int flags)
264 {
265 struct nouveau_pci_softc *const sc = device_private(self);
266 int error;
267
268 /* XXX Check for in-use before tearing it all down... */
269 error = config_detach_children(self, flags);
270 if (error)
271 return error;
272
273 pmf_device_deregister(self);
274 if (sc->sc_dev_registered)
275 drm_dev_unregister(sc->sc_drm_dev);
276 if (sc->sc_nvdev_inited)
277 nouveau_drm_device_fini(sc->sc_drm_dev);
278 if (sc->sc_pci_attached)
279 drm_pci_detach(sc->sc_drm_dev);
280 if (sc->sc_drm_dev) {
281 drm_dev_put(sc->sc_drm_dev);
282 sc->sc_drm_dev = NULL;
283 }
284 if (sc->sc_nv_dev)
285 nvkm_device_del(&sc->sc_nv_dev);
286 if (sc->sc_task_wq) {
287 workqueue_destroy(sc->sc_task_wq);
288 sc->sc_task_wq = NULL;
289 }
290 linux_pci_dev_destroy(&sc->sc_pci_dev);
291
292 return 0;
293 }
294
295 /*
296 * XXX Synchronize with nouveau_do_suspend in nouveau_drm.c.
297 */
298 static bool
299 nouveau_pci_suspend(device_t self, const pmf_qual_t *qual __unused)
300 {
301 struct nouveau_pci_softc *const sc = device_private(self);
302
303 return nouveau_pmops_suspend(sc->sc_drm_dev) == 0;
304 }
305
306 static bool
307 nouveau_pci_resume(device_t self, const pmf_qual_t *qual)
308 {
309 struct nouveau_pci_softc *const sc = device_private(self);
310
311 return nouveau_pmops_resume(sc->sc_drm_dev) == 0;
312 }
313
314 static void
315 nouveau_pci_task_work(struct work *work, void *cookie __unused)
316 {
317 struct nouveau_pci_task *const task = container_of(work,
318 struct nouveau_pci_task, nt_u.work);
319
320 (*task->nt_fn)(task);
321 }
322
323 int
324 nouveau_pci_task_schedule(device_t self, struct nouveau_pci_task *task)
325 {
326 struct nouveau_pci_softc *const sc = device_private(self);
327
328 if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp)
329 SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, nt_u.queue);
330 else
331 workqueue_enqueue(sc->sc_task_wq, &task->nt_u.work, NULL);
332
333 return 0;
334 }
335
336 extern struct drm_driver *const nouveau_drm_driver_stub; /* XXX */
337 extern struct drm_driver *const nouveau_drm_driver_pci; /* XXX */
338
339 static int
340 nouveau_pci_modcmd(modcmd_t cmd, void *arg __unused)
341 {
342
343 switch (cmd) {
344 case MODULE_CMD_INIT:
345 *nouveau_drm_driver_pci = *nouveau_drm_driver_stub;
346 nouveau_drm_driver_pci->request_irq = drm_pci_request_irq;
347 nouveau_drm_driver_pci->free_irq = drm_pci_free_irq;
348 #if 0 /* XXX nouveau acpi */
349 nouveau_register_dsm_handler();
350 #endif
351 break;
352 case MODULE_CMD_FINI:
353 #if 0 /* XXX nouveau acpi */
354 nouveau_unregister_dsm_handler();
355 #endif
356 break;
357 default:
358 return ENOTTY;
359 }
360
361 return 0;
362 }
363