1 | /* $NetBSD: radeon_pci.c,v 1.10 2015/05/29 05:48:46 mrg Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2014 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: radeon_pci.c,v 1.10 2015/05/29 05:48:46 mrg Exp $" ); |
34 | |
35 | #ifdef _KERNEL_OPT |
36 | #include "vga.h" |
37 | #endif |
38 | |
39 | #include <sys/types.h> |
40 | #include <sys/queue.h> |
41 | #include <sys/systm.h> |
42 | #include <sys/workqueue.h> |
43 | |
44 | #include <dev/pci/pciio.h> |
45 | #include <dev/pci/pcireg.h> |
46 | #include <dev/pci/pcivar.h> |
47 | |
48 | #include <dev/pci/wsdisplay_pci.h> |
49 | #include <dev/wsfb/genfbvar.h> |
50 | |
51 | #if NVGA > 0 |
52 | /* |
53 | * XXX All we really need is vga_is_console from vgavar.h, but the |
54 | * header files are missing their own dependencies, so we need to |
55 | * explicitly drag in the other crap. |
56 | */ |
57 | #include <dev/ic/mc6845reg.h> |
58 | #include <dev/ic/pcdisplayvar.h> |
59 | #include <dev/ic/vgareg.h> |
60 | #include <dev/ic/vgavar.h> |
61 | #endif |
62 | |
63 | #include <drm/drmP.h> |
64 | #include <drm/drm_fb_helper.h> |
65 | |
66 | #include <radeon.h> |
67 | #include "radeon_drv.h" |
68 | #include "radeon_task.h" |
69 | |
70 | SIMPLEQ_HEAD(radeon_task_head, radeon_task); |
71 | |
72 | struct radeon_softc { |
73 | device_t sc_dev; |
74 | struct pci_attach_args sc_pa; |
75 | enum { |
76 | RADEON_TASK_ATTACH, |
77 | RADEON_TASK_WORKQUEUE, |
78 | } sc_task_state; |
79 | union { |
80 | struct workqueue *workqueue; |
81 | struct radeon_task_head attach; |
82 | } sc_task_u; |
83 | struct drm_device *sc_drm_dev; |
84 | struct pci_dev sc_pci_dev; |
85 | #if defined(__i386__) |
86 | #define RADEON_PCI_UGLY_MAP_HACK |
87 | /* XXX Used to claim the VGA device before attach_real */ |
88 | bus_space_handle_t sc_temp_memh; |
89 | bool sc_temp_set; |
90 | #endif |
91 | }; |
92 | |
93 | struct radeon_device * |
94 | radeon_device_private(device_t self) |
95 | { |
96 | struct radeon_softc *const sc = device_private(self); |
97 | |
98 | return sc->sc_drm_dev->dev_private; |
99 | } |
100 | |
101 | static bool radeon_pci_lookup(const struct pci_attach_args *, |
102 | unsigned long *); |
103 | |
104 | static int radeon_match(device_t, cfdata_t, void *); |
105 | static void radeon_attach(device_t, device_t, void *); |
106 | static void radeon_attach_real(device_t); |
107 | static int radeon_detach(device_t, int); |
108 | static bool radeon_do_suspend(device_t, const pmf_qual_t *); |
109 | static bool radeon_do_resume(device_t, const pmf_qual_t *); |
110 | |
111 | static void radeon_task_work(struct work *, void *); |
112 | |
113 | CFATTACH_DECL_NEW(radeon, sizeof(struct radeon_softc), |
114 | radeon_match, radeon_attach, radeon_detach, NULL); |
115 | |
116 | /* XXX Kludge to get these from radeon_drv.c. */ |
117 | extern struct drm_driver *const radeon_drm_driver; |
118 | extern const struct pci_device_id *const radeon_device_ids; |
119 | extern const size_t radeon_n_device_ids; |
120 | |
121 | /* Set this to false if you want to match R100/R200 */ |
122 | bool radeon_pci_ignore_r100_r200 = true; |
123 | |
124 | static bool |
125 | radeon_pci_lookup(const struct pci_attach_args *pa, unsigned long *flags) |
126 | { |
127 | size_t i; |
128 | enum radeon_family fam; |
129 | |
130 | for (i = 0; i < radeon_n_device_ids; i++) { |
131 | if ((PCI_VENDOR(pa->pa_id) == radeon_device_ids[i].vendor) && |
132 | (PCI_PRODUCT(pa->pa_id) == radeon_device_ids[i].device)) |
133 | break; |
134 | } |
135 | |
136 | /* Did we find it? */ |
137 | if (i == radeon_n_device_ids) |
138 | return false; |
139 | |
140 | /* NetBSD drm2 fails on R100 and many R200 chipsets, disable for now */ |
141 | fam = radeon_device_ids[i].driver_data & RADEON_FAMILY_MASK; |
142 | if (radeon_pci_ignore_r100_r200 && fam < CHIP_RV280) |
143 | return false; |
144 | |
145 | if (flags) |
146 | *flags = radeon_device_ids[i].driver_data; |
147 | return true; |
148 | } |
149 | |
150 | static int |
151 | radeon_match(device_t parent, cfdata_t match, void *aux) |
152 | { |
153 | extern int radeon_guarantee_initialized(void); |
154 | const struct pci_attach_args *const pa = aux; |
155 | int error; |
156 | |
157 | error = radeon_guarantee_initialized(); |
158 | if (error) { |
159 | aprint_error("radeon: failed to initialize: %d\n" , error); |
160 | return 0; |
161 | } |
162 | |
163 | if (!radeon_pci_lookup(pa, NULL)) |
164 | return 0; |
165 | |
166 | return 6; /* XXX Beat genfb_pci... */ |
167 | } |
168 | |
169 | static void |
170 | radeon_attach(device_t parent, device_t self, void *aux) |
171 | { |
172 | struct radeon_softc *const sc = device_private(self); |
173 | const struct pci_attach_args *const pa = aux; |
174 | |
175 | pci_aprint_devinfo(pa, NULL); |
176 | |
177 | if (!pmf_device_register(self, &radeon_do_suspend, &radeon_do_resume)) |
178 | aprint_error_dev(self, "unable to establish power handler\n" ); |
179 | |
180 | /* |
181 | * Trivial initialization first; the rest will come after we |
182 | * have mounted the root file system and can load firmware |
183 | * images. |
184 | */ |
185 | sc->sc_dev = NULL; |
186 | sc->sc_pa = *pa; |
187 | |
188 | #ifdef RADEON_PCI_UGLY_MAP_HACK |
189 | /* |
190 | * XXX |
191 | * We try to map the VGA registers, in case we can prevent vga@isa or |
192 | * pcdisplay@isa attaching, and stealing wsdisplay0. This only works |
193 | * with serial console, as actual VGA console has already mapped them. |
194 | * The only way to handle that is for vga@isa to not attach. |
195 | */ |
196 | int rv = bus_space_map(pa->pa_memt, 0xb0000, 0x10000, 0, |
197 | &sc->sc_temp_memh); |
198 | sc->sc_temp_set = rv == 0; |
199 | if (rv != 0) |
200 | aprint_error_dev(self, "unable to reserve VGA registers for " |
201 | "i386 radeondrmkms hack\n" ); |
202 | #endif |
203 | |
204 | config_mountroot(self, &radeon_attach_real); |
205 | } |
206 | |
207 | static void |
208 | radeon_attach_real(device_t self) |
209 | { |
210 | struct radeon_softc *const sc = device_private(self); |
211 | const struct pci_attach_args *const pa = &sc->sc_pa; |
212 | bool ok __diagused; |
213 | unsigned long flags; |
214 | int error; |
215 | |
216 | ok = radeon_pci_lookup(pa, &flags); |
217 | KASSERT(ok); |
218 | |
219 | #ifdef RADEON_PCI_UGLY_MAP_HACK |
220 | /* |
221 | * XXX |
222 | * Unmap the VGA registers. |
223 | */ |
224 | if (sc->sc_temp_set) |
225 | bus_space_unmap(pa->pa_memt, sc->sc_temp_memh, 0x10000); |
226 | #endif |
227 | |
228 | sc->sc_task_state = RADEON_TASK_ATTACH; |
229 | SIMPLEQ_INIT(&sc->sc_task_u.attach); |
230 | |
231 | /* XXX errno Linux->NetBSD */ |
232 | error = -drm_pci_attach(self, pa, &sc->sc_pci_dev, radeon_drm_driver, |
233 | flags, &sc->sc_drm_dev); |
234 | if (error) { |
235 | aprint_error_dev(self, "unable to attach drm: %d\n" , error); |
236 | goto out; |
237 | } |
238 | |
239 | while (!SIMPLEQ_EMPTY(&sc->sc_task_u.attach)) { |
240 | struct radeon_task *const task = |
241 | SIMPLEQ_FIRST(&sc->sc_task_u.attach); |
242 | |
243 | SIMPLEQ_REMOVE_HEAD(&sc->sc_task_u.attach, rt_u.queue); |
244 | (*task->rt_fn)(task); |
245 | } |
246 | |
247 | sc->sc_task_state = RADEON_TASK_WORKQUEUE; |
248 | error = workqueue_create(&sc->sc_task_u.workqueue, "radeonfb" , |
249 | &radeon_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); |
250 | if (error) { |
251 | aprint_error_dev(self, "unable to create workqueue: %d\n" , |
252 | error); |
253 | sc->sc_task_u.workqueue = NULL; |
254 | goto out; |
255 | } |
256 | |
257 | out: sc->sc_dev = self; |
258 | } |
259 | |
260 | static int |
261 | radeon_detach(device_t self, int flags) |
262 | { |
263 | struct radeon_softc *const sc = device_private(self); |
264 | int error; |
265 | |
266 | if (sc->sc_dev == NULL) |
267 | /* Not done attaching. */ |
268 | return EBUSY; |
269 | |
270 | /* XXX Check for in-use before tearing it all down... */ |
271 | error = config_detach_children(self, flags); |
272 | if (error) |
273 | return error; |
274 | |
275 | if (sc->sc_task_state == RADEON_TASK_ATTACH) |
276 | goto out; |
277 | if (sc->sc_task_u.workqueue != NULL) { |
278 | workqueue_destroy(sc->sc_task_u.workqueue); |
279 | sc->sc_task_u.workqueue = NULL; |
280 | } |
281 | |
282 | if (sc->sc_drm_dev == NULL) |
283 | goto out; |
284 | /* XXX errno Linux->NetBSD */ |
285 | error = -drm_pci_detach(sc->sc_drm_dev, flags); |
286 | if (error) |
287 | /* XXX Kinda too late to fail now... */ |
288 | return error; |
289 | sc->sc_drm_dev = NULL; |
290 | |
291 | out: pmf_device_deregister(self); |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static bool |
297 | radeon_do_suspend(device_t self, const pmf_qual_t *qual) |
298 | { |
299 | struct radeon_softc *const sc = device_private(self); |
300 | struct drm_device *const dev = sc->sc_drm_dev; |
301 | int ret; |
302 | bool is_console = true; /* XXX */ |
303 | |
304 | if (dev == NULL) |
305 | return true; |
306 | |
307 | ret = radeon_suspend_kms(dev, true, is_console); |
308 | if (ret) |
309 | return false; |
310 | |
311 | return true; |
312 | } |
313 | |
314 | static bool |
315 | radeon_do_resume(device_t self, const pmf_qual_t *qual) |
316 | { |
317 | struct radeon_softc *const sc = device_private(self); |
318 | struct drm_device *const dev = sc->sc_drm_dev; |
319 | int ret; |
320 | bool is_console = true; /* XXX */ |
321 | |
322 | if (dev == NULL) |
323 | return true; |
324 | |
325 | ret = radeon_resume_kms(dev, true, is_console); |
326 | if (ret) |
327 | return false; |
328 | |
329 | return true; |
330 | } |
331 | |
332 | static void |
333 | radeon_task_work(struct work *work, void *cookie __unused) |
334 | { |
335 | struct radeon_task *const task = container_of(work, struct radeon_task, |
336 | rt_u.work); |
337 | |
338 | (*task->rt_fn)(task); |
339 | } |
340 | |
341 | int |
342 | radeon_task_schedule(device_t self, struct radeon_task *task) |
343 | { |
344 | struct radeon_softc *const sc = device_private(self); |
345 | |
346 | switch (sc->sc_task_state) { |
347 | case RADEON_TASK_ATTACH: |
348 | SIMPLEQ_INSERT_TAIL(&sc->sc_task_u.attach, task, rt_u.queue); |
349 | return 0; |
350 | case RADEON_TASK_WORKQUEUE: |
351 | if (sc->sc_task_u.workqueue == NULL) { |
352 | aprint_error_dev(self, "unable to schedule task\n" ); |
353 | return EIO; |
354 | } |
355 | workqueue_enqueue(sc->sc_task_u.workqueue, &task->rt_u.work, |
356 | NULL); |
357 | return 0; |
358 | default: |
359 | panic("radeon in invalid task state: %d\n" , |
360 | (int)sc->sc_task_state); |
361 | } |
362 | } |
363 | |