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