1 | /* $NetBSD: radeondrmkmsfb.c,v 1.6 2015/11/05 20:52: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 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: radeondrmkmsfb.c,v 1.6 2015/11/05 20:52:46 mrg Exp $" ); |
35 | |
36 | #ifdef _KERNEL_OPT |
37 | #include "vga.h" |
38 | #endif |
39 | |
40 | #include <sys/types.h> |
41 | #include <sys/device.h> |
42 | |
43 | #include <dev/pci/pciio.h> |
44 | #include <dev/pci/pcireg.h> |
45 | #include <dev/pci/pcivar.h> |
46 | |
47 | #include <dev/pci/wsdisplay_pci.h> |
48 | #include <dev/wsfb/genfbvar.h> |
49 | |
50 | #if NVGA > 0 |
51 | /* |
52 | * XXX All we really need is vga_is_console from vgavar.h, but the |
53 | * header files are missing their own dependencies, so we need to |
54 | * explicitly drag in the other crap. |
55 | */ |
56 | #include <dev/ic/mc6845reg.h> |
57 | #include <dev/ic/pcdisplayvar.h> |
58 | #include <dev/ic/vgareg.h> |
59 | #include <dev/ic/vgavar.h> |
60 | #endif |
61 | |
62 | #include <drm/drmP.h> |
63 | #include <drm/drm_fb_helper.h> |
64 | |
65 | #include <radeon.h> |
66 | #include "radeon_drv.h" |
67 | #include "radeon_task.h" |
68 | #include "radeondrmkmsfb.h" |
69 | |
70 | struct radeonfb_softc { |
71 | /* XXX genfb requires the genfb_softc to be first. */ |
72 | struct genfb_softc sc_genfb; |
73 | device_t sc_dev; |
74 | struct radeonfb_attach_args sc_rfa; |
75 | struct radeon_task sc_setconfig_task; |
76 | bool sc_scheduled:1; |
77 | bool sc_attached:1; |
78 | }; |
79 | |
80 | static int radeonfb_match(device_t, cfdata_t, void *); |
81 | static void radeonfb_attach(device_t, device_t, void *); |
82 | static int radeonfb_detach(device_t, int); |
83 | |
84 | static void radeonfb_setconfig_task(struct radeon_task *); |
85 | |
86 | static int radeonfb_genfb_ioctl(void *, void *, unsigned long, void *, |
87 | int, struct lwp *); |
88 | static paddr_t radeonfb_genfb_mmap(void *, void *, off_t, int); |
89 | static int radeonfb_genfb_enable_polling(void *); |
90 | static int radeonfb_genfb_disable_polling(void *); |
91 | static bool radeonfb_genfb_shutdown(device_t, int); |
92 | static bool radeonfb_genfb_setmode(struct genfb_softc *, int); |
93 | |
94 | static const struct genfb_mode_callback radeonfb_genfb_mode_callback = { |
95 | .gmc_setmode = radeonfb_genfb_setmode, |
96 | }; |
97 | |
98 | CFATTACH_DECL_NEW(radeondrmkmsfb, sizeof(struct radeonfb_softc), |
99 | radeonfb_match, radeonfb_attach, radeonfb_detach, NULL); |
100 | |
101 | static int |
102 | radeonfb_match(device_t parent, cfdata_t match, void *aux) |
103 | { |
104 | |
105 | return 1; |
106 | } |
107 | |
108 | static void |
109 | radeonfb_attach(device_t parent, device_t self, void *aux) |
110 | { |
111 | struct radeonfb_softc *const sc = device_private(self); |
112 | const struct radeonfb_attach_args *const rfa = aux; |
113 | int error; |
114 | |
115 | sc->sc_dev = self; |
116 | sc->sc_rfa = *rfa; |
117 | sc->sc_scheduled = false; |
118 | sc->sc_attached = false; |
119 | |
120 | aprint_naive("\n" ); |
121 | aprint_normal("\n" ); |
122 | |
123 | radeon_task_init(&sc->sc_setconfig_task, &radeonfb_setconfig_task); |
124 | error = radeon_task_schedule(parent, &sc->sc_setconfig_task); |
125 | if (error) { |
126 | aprint_error_dev(self, "failed to schedule mode set: %d\n" , |
127 | error); |
128 | goto fail0; |
129 | } |
130 | sc->sc_scheduled = true; |
131 | |
132 | /* Success! */ |
133 | return; |
134 | |
135 | fail0: return; |
136 | } |
137 | |
138 | static int |
139 | radeonfb_detach(device_t self, int flags) |
140 | { |
141 | struct radeonfb_softc *const sc = device_private(self); |
142 | |
143 | if (sc->sc_scheduled) |
144 | return EBUSY; |
145 | |
146 | if (sc->sc_attached) { |
147 | /* XXX genfb detach? Help? */ |
148 | sc->sc_attached = false; |
149 | } |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | static void |
155 | radeonfb_setconfig_task(struct radeon_task *task) |
156 | { |
157 | struct radeonfb_softc *const sc = container_of(task, |
158 | struct radeonfb_softc, sc_setconfig_task); |
159 | const prop_dictionary_t dict = device_properties(sc->sc_dev); |
160 | const struct radeonfb_attach_args *const rfa = &sc->sc_rfa; |
161 | const struct drm_fb_helper_surface_size *const sizes = |
162 | &rfa->rfa_fb_sizes; |
163 | enum { CONS_VGA, CONS_GENFB, CONS_NONE } what_was_cons; |
164 | static const struct genfb_ops zero_genfb_ops; |
165 | struct genfb_ops genfb_ops = zero_genfb_ops; |
166 | int error; |
167 | |
168 | KASSERT(sc->sc_scheduled); |
169 | |
170 | /* XXX Ugh... Pass these parameters some other way! */ |
171 | prop_dictionary_set_uint32(dict, "width" , sizes->surface_width); |
172 | prop_dictionary_set_uint32(dict, "height" , sizes->surface_height); |
173 | prop_dictionary_set_uint8(dict, "depth" , sizes->surface_bpp); |
174 | prop_dictionary_set_uint16(dict, "linebytes" , rfa->rfa_fb_linebytes); |
175 | prop_dictionary_set_uint32(dict, "address" , 0); /* XXX >32-bit */ |
176 | CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t)); |
177 | prop_dictionary_set_uint64(dict, "virtual_address" , |
178 | (uint64_t)(uintptr_t)rfa->rfa_fb_ptr); |
179 | |
180 | prop_dictionary_set_uint64(dict, "mode_callback" , |
181 | (uint64_t)(uintptr_t)&radeonfb_genfb_mode_callback); |
182 | |
183 | /* XXX Whattakludge! */ |
184 | #if NVGA > 0 |
185 | if (vga_is_console(rfa->rfa_fb_helper->dev->pdev->pd_pa.pa_iot, -1)) { |
186 | what_was_cons = CONS_VGA; |
187 | prop_dictionary_set_bool(dict, "is_console" , true); |
188 | vga_cndetach(); |
189 | } else |
190 | #endif |
191 | if (genfb_is_console() && genfb_is_enabled()) { |
192 | what_was_cons = CONS_GENFB; |
193 | prop_dictionary_set_bool(dict, "is_console" , true); |
194 | } else { |
195 | what_was_cons = CONS_NONE; |
196 | prop_dictionary_set_bool(dict, "is_console" , false); |
197 | } |
198 | |
199 | sc->sc_genfb.sc_dev = sc->sc_dev; |
200 | genfb_init(&sc->sc_genfb); |
201 | genfb_ops.genfb_ioctl = radeonfb_genfb_ioctl; |
202 | genfb_ops.genfb_mmap = radeonfb_genfb_mmap; |
203 | genfb_ops.genfb_enable_polling = radeonfb_genfb_enable_polling; |
204 | genfb_ops.genfb_disable_polling = radeonfb_genfb_disable_polling; |
205 | |
206 | error = genfb_attach(&sc->sc_genfb, &genfb_ops); |
207 | if (error) { |
208 | aprint_error_dev(sc->sc_dev, "failed to attach genfb: %d\n" , |
209 | error); |
210 | goto fail0; |
211 | } |
212 | sc->sc_attached = true; |
213 | |
214 | pmf_device_register1(sc->sc_dev, NULL, NULL, |
215 | radeonfb_genfb_shutdown); |
216 | |
217 | /* Success! */ |
218 | sc->sc_scheduled = false; |
219 | return; |
220 | |
221 | fail0: /* XXX Restore console... */ |
222 | switch (what_was_cons) { |
223 | case CONS_VGA: |
224 | break; |
225 | case CONS_GENFB: |
226 | break; |
227 | case CONS_NONE: |
228 | break; |
229 | default: |
230 | break; |
231 | } |
232 | } |
233 | |
234 | static int |
235 | radeonfb_genfb_ioctl(void *v, void *vs, unsigned long cmd, void *data, int flag, |
236 | struct lwp *l) |
237 | { |
238 | struct genfb_softc *const genfb = v; |
239 | struct radeonfb_softc *const sc = container_of(genfb, |
240 | struct radeonfb_softc, sc_genfb); |
241 | struct drm_device *const dev = sc->sc_rfa.rfa_fb_helper->dev; |
242 | const struct pci_attach_args *const pa = &dev->pdev->pd_pa; |
243 | |
244 | switch (cmd) { |
245 | case WSDISPLAYIO_GTYPE: |
246 | *(unsigned int *)data = WSDISPLAY_TYPE_PCIVGA; |
247 | return 0; |
248 | |
249 | /* PCI config read/write passthrough. */ |
250 | case PCI_IOC_CFGREAD: |
251 | case PCI_IOC_CFGWRITE: |
252 | return pci_devioctl(pa->pa_pc, pa->pa_tag, cmd, data, flag, l); |
253 | |
254 | case WSDISPLAYIO_GET_BUSID: |
255 | return wsdisplayio_busid_pci(dev->dev, pa->pa_pc, pa->pa_tag, |
256 | data); |
257 | |
258 | default: |
259 | return EPASSTHROUGH; |
260 | } |
261 | } |
262 | |
263 | static paddr_t |
264 | radeonfb_genfb_mmap(void *v, void *vs, off_t offset, int prot) |
265 | { |
266 | struct genfb_softc *const genfb = v; |
267 | struct radeonfb_softc *const sc = container_of(genfb, |
268 | struct radeonfb_softc, sc_genfb); |
269 | struct drm_fb_helper *const helper = sc->sc_rfa.rfa_fb_helper; |
270 | struct drm_framebuffer *const fb = helper->fb; |
271 | struct radeon_framebuffer *const rfb = container_of(fb, |
272 | struct radeon_framebuffer, base); |
273 | struct drm_gem_object *const gobj = rfb->obj; |
274 | struct radeon_bo *const rbo = gem_to_radeon_bo(gobj); |
275 | struct drm_device *const dev = helper->dev; |
276 | const struct pci_attach_args *const pa = &dev->pdev->pd_pa; |
277 | unsigned int i; |
278 | |
279 | if (offset < 0) |
280 | return -1; |
281 | |
282 | /* Treat low memory as the framebuffer itself. */ |
283 | if (offset < genfb->sc_fbsize) { |
284 | const unsigned num_pages __diagused = rbo->tbo.num_pages; |
285 | int flags = 0; |
286 | |
287 | KASSERT(genfb->sc_fbsize == (num_pages << PAGE_SHIFT)); |
288 | KASSERT(rbo->tbo.mem.bus.is_iomem); |
289 | |
290 | if (ISSET(rbo->tbo.mem.placement, TTM_PL_FLAG_WC)) |
291 | flags |= BUS_SPACE_MAP_PREFETCHABLE; |
292 | |
293 | return bus_space_mmap(rbo->tbo.bdev->memt, |
294 | rbo->tbo.mem.bus.base, rbo->tbo.mem.bus.offset + offset, |
295 | prot, flags); |
296 | } |
297 | |
298 | /* XXX Cargo-culted from genfb_pci. */ |
299 | if (kauth_authorize_machdep(kauth_cred_get(), |
300 | KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) != 0) { |
301 | aprint_normal_dev(dev->dev, "mmap at %" PRIxMAX" rejected\n" , |
302 | (uintmax_t)offset); |
303 | return -1; |
304 | } |
305 | |
306 | for (i = 0; PCI_BAR(i) <= PCI_MAPREG_ROM; i++) { |
307 | pcireg_t type; |
308 | bus_addr_t addr; |
309 | bus_size_t size; |
310 | int flags; |
311 | |
312 | /* Interrogate the BAR. */ |
313 | if (!pci_mapreg_probe(pa->pa_pc, pa->pa_tag, PCI_BAR(i), |
314 | &type)) |
315 | continue; |
316 | if (PCI_MAPREG_TYPE(type) != PCI_MAPREG_TYPE_MEM) |
317 | continue; |
318 | if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, PCI_BAR(i), type, |
319 | &addr, &size, &flags)) |
320 | continue; |
321 | |
322 | /* Try to map it if it's in range. */ |
323 | if ((addr <= offset) && (offset < (addr + size))) |
324 | return bus_space_mmap(pa->pa_memt, addr, |
325 | (offset - addr), prot, flags); |
326 | |
327 | /* Skip a slot if this was a 64-bit BAR. */ |
328 | if ((PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_MEM) && |
329 | (PCI_MAPREG_MEM_TYPE(type) == PCI_MAPREG_MEM_TYPE_64BIT)) |
330 | i += 1; |
331 | } |
332 | |
333 | /* Failure! */ |
334 | return -1; |
335 | } |
336 | |
337 | static int |
338 | radeonfb_genfb_enable_polling(void *cookie) |
339 | { |
340 | struct genfb_softc *const genfb = cookie; |
341 | struct radeonfb_softc *const sc = container_of(genfb, |
342 | struct radeonfb_softc, sc_genfb); |
343 | |
344 | return drm_fb_helper_debug_enter_fb(sc->sc_rfa.rfa_fb_helper); |
345 | } |
346 | |
347 | static int |
348 | radeonfb_genfb_disable_polling(void *cookie) |
349 | { |
350 | struct genfb_softc *const genfb = cookie; |
351 | struct radeonfb_softc *const sc = container_of(genfb, |
352 | struct radeonfb_softc, sc_genfb); |
353 | |
354 | return drm_fb_helper_debug_leave_fb(sc->sc_rfa.rfa_fb_helper); |
355 | } |
356 | |
357 | static bool |
358 | radeonfb_genfb_shutdown(device_t self, int flags) |
359 | { |
360 | genfb_enable_polling(self); |
361 | return true; |
362 | } |
363 | |
364 | static bool |
365 | radeonfb_genfb_setmode(struct genfb_softc *genfb, int mode) |
366 | { |
367 | struct radeonfb_softc *sc = (struct radeonfb_softc *)genfb; |
368 | |
369 | if (mode == WSDISPLAYIO_MODE_EMUL) { |
370 | drm_fb_helper_set_config(sc->sc_rfa.rfa_fb_helper); |
371 | } |
372 | |
373 | return true; |
374 | } |
375 | |