xref: /netbsd-src/sys/external/bsd/drm2/amdgpu/amdgpu_pci.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /*	$NetBSD: amdgpu_pci.c,v 1.10 2021/12/19 12:28:12 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2018 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: amdgpu_pci.c,v 1.10 2021/12/19 12:28:12 riastradh Exp $");
34 
35 #include <sys/types.h>
36 #include <sys/atomic.h>
37 #include <sys/queue.h>
38 #include <sys/systm.h>
39 #include <sys/workqueue.h>
40 
41 #include <dev/pci/pcivar.h>
42 
43 #include <linux/pci.h>
44 
45 #include <drm/drm_device.h>
46 #include <drm/drm_drv.h>
47 #include <drm/drm_fb_helper.h>
48 #include <drm/drm_pci.h>
49 
50 #include <amdgpu.h>
51 #include "amdgpu_drv.h"
52 #include "amdgpu_task.h"
53 
54 struct drm_device;
55 
56 SIMPLEQ_HEAD(amdgpu_task_head, amdgpu_task);
57 
58 struct amdgpu_softc {
59 	device_t			sc_dev;
60 	struct pci_attach_args		sc_pa;
61 	struct lwp			*sc_task_thread;
62 	struct amdgpu_task_head		sc_tasks;
63 	struct workqueue		*sc_task_wq;
64 	struct drm_device		*sc_drm_dev;
65 	struct pci_dev			sc_pci_dev;
66 	bool				sc_pci_attached;
67 	bool				sc_dev_registered;
68 };
69 
70 static bool	amdgpu_pci_lookup(const struct pci_attach_args *,
71 		    unsigned long *);
72 
73 static int	amdgpu_match(device_t, cfdata_t, void *);
74 static void	amdgpu_attach(device_t, device_t, void *);
75 static void	amdgpu_attach_real(device_t);
76 static int	amdgpu_detach(device_t, int);
77 static bool	amdgpu_do_suspend(device_t, const pmf_qual_t *);
78 static bool	amdgpu_do_resume(device_t, const pmf_qual_t *);
79 
80 static void	amdgpu_task_work(struct work *, void *);
81 
82 CFATTACH_DECL_NEW(amdgpu, sizeof(struct amdgpu_softc),
83     amdgpu_match, amdgpu_attach, amdgpu_detach, NULL);
84 
85 /* XXX Kludge to get these from amdgpu_drv.c.  */
86 extern struct drm_driver *const amdgpu_drm_driver;
87 extern const struct pci_device_id *const amdgpu_device_ids;
88 extern const size_t amdgpu_n_device_ids;
89 
90 static bool
91 amdgpu_pci_lookup(const struct pci_attach_args *pa, unsigned long *flags)
92 {
93 	size_t i;
94 
95 	for (i = 0; i < amdgpu_n_device_ids; i++) {
96 		if ((PCI_VENDOR(pa->pa_id) == amdgpu_device_ids[i].vendor) &&
97 		    (PCI_PRODUCT(pa->pa_id) == amdgpu_device_ids[i].device))
98 			break;
99 	}
100 
101 	/* Did we find it?  */
102 	if (i == amdgpu_n_device_ids)
103 		return false;
104 
105 	if (flags)
106 		*flags = amdgpu_device_ids[i].driver_data;
107 	return true;
108 }
109 
110 static int
111 amdgpu_match(device_t parent, cfdata_t match, void *aux)
112 {
113 	extern int amdgpu_guarantee_initialized(void);
114 	const struct pci_attach_args *const pa = aux;
115 	int error;
116 
117 	error = amdgpu_guarantee_initialized();
118 	if (error) {
119 		aprint_error("amdgpu: failed to initialize: %d\n", error);
120 		return 0;
121 	}
122 
123 	if (!amdgpu_pci_lookup(pa, NULL))
124 		return 0;
125 
126 	return 7;		/* beat genfb_pci and radeon  */
127 }
128 
129 static void
130 amdgpu_attach(device_t parent, device_t self, void *aux)
131 {
132 	struct amdgpu_softc *const sc = device_private(self);
133 	const struct pci_attach_args *const pa = aux;
134 	int error;
135 
136 	pci_aprint_devinfo(pa, NULL);
137 
138 	/* Initialize the Linux PCI device descriptor.  */
139 	linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0);
140 
141 	sc->sc_dev = self;
142 	sc->sc_pa = *pa;
143 	sc->sc_task_thread = NULL;
144 	SIMPLEQ_INIT(&sc->sc_tasks);
145 	error = workqueue_create(&sc->sc_task_wq, "amdgpufb",
146 	    &amdgpu_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE);
147 	if (error) {
148 		aprint_error_dev(self, "unable to create workqueue: %d\n",
149 		    error);
150 		sc->sc_task_wq = NULL;
151 		return;
152 	}
153 
154 	/*
155 	 * Defer the remainder of initialization until we have mounted
156 	 * the root file system and can load firmware images.
157 	 */
158 	config_mountroot(self, &amdgpu_attach_real);
159 }
160 
161 static void
162 amdgpu_attach_real(device_t self)
163 {
164 	struct amdgpu_softc *const sc = device_private(self);
165 	const struct pci_attach_args *const pa = &sc->sc_pa;
166 	bool ok __diagused;
167 	unsigned long flags = 0; /* XXXGCC */
168 	int error;
169 
170 	ok = amdgpu_pci_lookup(pa, &flags);
171 	KASSERT(ok);
172 
173 	/*
174 	 * Cause any tasks issued synchronously during attach to be
175 	 * processed at the end of this function.
176 	 */
177 	sc->sc_task_thread = curlwp;
178 
179 	sc->sc_drm_dev = drm_dev_alloc(amdgpu_drm_driver, self);
180 	if (IS_ERR(sc->sc_drm_dev)) {
181 		aprint_error_dev(self, "unable to create drm device: %ld\n",
182 		    PTR_ERR(sc->sc_drm_dev));
183 		sc->sc_drm_dev = NULL;
184 		goto out;
185 	}
186 
187 	/* XXX errno Linux->NetBSD */
188 	error = -drm_pci_attach(sc->sc_drm_dev, &sc->sc_pci_dev);
189 	if (error) {
190 		aprint_error_dev(self, "unable to attach drm: %d\n", error);
191 		goto out;
192 	}
193 	sc->sc_pci_attached = true;
194 
195 	/* XXX errno Linux->NetBSD */
196 	error = -drm_dev_register(sc->sc_drm_dev, flags);
197 	if (error) {
198 		aprint_error_dev(self, "unable to register drm: %d\n", error);
199 		goto out;
200 	}
201 	sc->sc_dev_registered = true;
202 
203 	if (!pmf_device_register(self, &amdgpu_do_suspend, &amdgpu_do_resume))
204 		aprint_error_dev(self, "unable to establish power handler\n");
205 
206 	/*
207 	 * Process asynchronous tasks queued synchronously during
208 	 * attach.  This will be for display detection to attach a
209 	 * framebuffer, so we have the opportunity for a console device
210 	 * to attach before autoconf has completed, in time for init(8)
211 	 * to find that console without panicking.
212 	 */
213 	while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) {
214 		struct amdgpu_task *const task = SIMPLEQ_FIRST(&sc->sc_tasks);
215 
216 		SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, rt_u.queue);
217 		(*task->rt_fn)(task);
218 	}
219 
220 out:	/* Cause any subesquent tasks to be processed by the workqueue.  */
221 	atomic_store_relaxed(&sc->sc_task_thread, NULL);
222 }
223 
224 static int
225 amdgpu_detach(device_t self, int flags)
226 {
227 	struct amdgpu_softc *const sc = device_private(self);
228 	int error;
229 
230 	/* XXX Check for in-use before tearing it all down...  */
231 	error = config_detach_children(self, flags);
232 	if (error)
233 		return error;
234 
235 	KASSERT(sc->sc_task_thread == NULL);
236 	KASSERT(SIMPLEQ_EMPTY(&sc->sc_tasks));
237 
238 	pmf_device_deregister(self);
239 	if (sc->sc_dev_registered)
240 		drm_dev_unregister(sc->sc_drm_dev);
241 	if (sc->sc_pci_attached)
242 		drm_pci_detach(sc->sc_drm_dev);
243 	if (sc->sc_drm_dev) {
244 		drm_dev_put(sc->sc_drm_dev);
245 		sc->sc_drm_dev = NULL;
246 	}
247 	if (sc->sc_task_wq) {
248 		workqueue_destroy(sc->sc_task_wq);
249 		sc->sc_task_wq = NULL;
250 	}
251 	linux_pci_dev_destroy(&sc->sc_pci_dev);
252 
253 	return 0;
254 }
255 
256 static bool
257 amdgpu_do_suspend(device_t self, const pmf_qual_t *qual)
258 {
259 	struct amdgpu_softc *const sc = device_private(self);
260 	struct drm_device *const dev = sc->sc_drm_dev;
261 	int ret;
262 
263 	ret = amdgpu_device_suspend(dev, /*fbcon*/true);
264 	if (ret)
265 		return false;
266 
267 	return true;
268 }
269 
270 static bool
271 amdgpu_do_resume(device_t self, const pmf_qual_t *qual)
272 {
273 	struct amdgpu_softc *const sc = device_private(self);
274 	struct drm_device *const dev = sc->sc_drm_dev;
275 	int ret;
276 
277 	ret = amdgpu_device_resume(dev, /*fbcon*/true);
278 	if (ret)
279 		return false;
280 
281 	return true;
282 }
283 
284 static void
285 amdgpu_task_work(struct work *work, void *cookie __unused)
286 {
287 	struct amdgpu_task *const task = container_of(work, struct amdgpu_task,
288 	    rt_u.work);
289 
290 	(*task->rt_fn)(task);
291 }
292 
293 int
294 amdgpu_task_schedule(device_t self, struct amdgpu_task *task)
295 {
296 	struct amdgpu_softc *const sc = device_private(self);
297 
298 	if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp)
299 		SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, rt_u.queue);
300 	else
301 		workqueue_enqueue(sc->sc_task_wq, &task->rt_u.work, NULL);
302 
303 	return 0;
304 }
305