intelfb.c revision 1.7 1 /* $NetBSD: intelfb.c,v 1.7 2014/08/06 22:16:38 jmcneill 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: intelfb.c,v 1.7 2014/08/06 22:16:38 jmcneill Exp $");
34
35 #ifdef _KERNEL_OPT
36 #include "vga.h"
37 #endif
38
39 #include <sys/types.h>
40 #include <sys/bus.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 "i915_drv.h"
66 #include "i915_pci.h"
67 #include "intelfb.h"
68
69 struct intelfb_softc {
70 /* XXX genfb requires the genfb_softc to be first. */
71 struct genfb_softc sc_genfb;
72 device_t sc_dev;
73 struct intelfb_attach_args sc_ifa;
74 bus_space_handle_t sc_fb_bsh;
75 struct i915drmkms_task sc_setconfig_task;
76 bool sc_mapped:1;
77 bool sc_scheduled:1;
78 bool sc_attached:1;
79 };
80
81 static int intelfb_match(device_t, cfdata_t, void *);
82 static void intelfb_attach(device_t, device_t, void *);
83 static int intelfb_detach(device_t, int);
84
85 static void intelfb_setconfig_task(struct i915drmkms_task *);
86
87 static int intelfb_genfb_dpms(struct drm_device *, int);
88 static int intelfb_genfb_ioctl(void *, void *, unsigned long, void *,
89 int, struct lwp *);
90 static paddr_t intelfb_genfb_mmap(void *, void *, off_t, int);
91 static int intelfb_genfb_enable_polling(void *);
92 static int intelfb_genfb_disable_polling(void *);
93 static bool intelfb_genfb_setmode(struct genfb_softc *, int);
94
95 static const struct genfb_mode_callback intelfb_genfb_mode_callback = {
96 .gmc_setmode = intelfb_genfb_setmode,
97 };
98
99 CFATTACH_DECL_NEW(intelfb, sizeof(struct intelfb_softc),
100 intelfb_match, intelfb_attach, intelfb_detach, NULL);
101
102 static int
103 intelfb_match(device_t parent, cfdata_t match, void *aux)
104 {
105
106 return 1;
107 }
108
109 static void
110 intelfb_attach(device_t parent, device_t self, void *aux)
111 {
112 struct intelfb_softc *const sc = device_private(self);
113 const struct intelfb_attach_args *const ifa = aux;
114 int error;
115
116 sc->sc_dev = self;
117 sc->sc_ifa = *ifa;
118 sc->sc_mapped = false;
119 sc->sc_scheduled = false;
120 sc->sc_attached = false;
121
122 aprint_naive("\n");
123 aprint_normal("\n");
124
125 /* XXX Defer this too? */
126 error = bus_space_map(ifa->ifa_fb_bst, ifa->ifa_fb_addr,
127 ifa->ifa_fb_size,
128 (BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE),
129 &sc->sc_fb_bsh);
130 if (error) {
131 aprint_error_dev(self, "unable to map framebuffer: %d\n",
132 error);
133 goto fail0;
134 }
135 sc->sc_mapped = true;
136
137 i915drmkms_task_init(&sc->sc_setconfig_task, &intelfb_setconfig_task);
138 error = i915drmkms_task_schedule(parent, &sc->sc_setconfig_task);
139 if (error) {
140 aprint_error_dev(self, "failed to schedule mode set: %d\n",
141 error);
142 goto fail1;
143 }
144 sc->sc_scheduled = true;
145
146 /* Success! */
147 return;
148
149 fail1: bus_space_unmap(ifa->ifa_fb_bst, sc->sc_fb_bsh, ifa->ifa_fb_size);
150 sc->sc_mapped = false;
151 fail0: return;
152 }
153
154 static int
155 intelfb_detach(device_t self, int flags)
156 {
157 struct intelfb_softc *const sc = device_private(self);
158
159 if (sc->sc_scheduled)
160 return EBUSY;
161
162 if (sc->sc_attached) {
163 /* XXX genfb detach? Help? */
164 sc->sc_attached = false;
165 }
166
167 if (sc->sc_mapped) {
168 bus_space_unmap(sc->sc_ifa.ifa_fb_bst, sc->sc_fb_bsh,
169 sc->sc_ifa.ifa_fb_size);
170 sc->sc_mapped = false;
171 }
172
173 return 0;
174 }
175
176 static void
177 intelfb_setconfig_task(struct i915drmkms_task *task)
178 {
179 struct intelfb_softc *const sc = container_of(task,
180 struct intelfb_softc, sc_setconfig_task);
181 const prop_dictionary_t dict = device_properties(sc->sc_dev);
182 const struct intelfb_attach_args *const ifa = &sc->sc_ifa;
183 const struct drm_fb_helper_surface_size *const sizes =
184 &ifa->ifa_fb_sizes;
185 #if NVGA > 0 /* XXX no workie for modules */
186 struct drm_device *const dev = sc->sc_ifa.ifa_drm_dev;
187 #endif
188 enum { CONS_VGA, CONS_GENFB, CONS_NONE } what_was_cons;
189 static const struct genfb_ops zero_genfb_ops;
190 struct genfb_ops genfb_ops = zero_genfb_ops;
191 int error;
192
193 KASSERT(sc->sc_scheduled);
194
195 if (ifa->ifa_fb_zero)
196 bus_space_set_region_1(sc->sc_ifa.ifa_fb_bst, sc->sc_fb_bsh, 0,
197 0, sc->sc_ifa.ifa_fb_size);
198
199 /* XXX Ugh... Pass these parameters some other way! */
200 prop_dictionary_set_uint32(dict, "width", sizes->surface_width);
201 prop_dictionary_set_uint32(dict, "height", sizes->surface_height);
202 prop_dictionary_set_uint8(dict, "depth", sizes->surface_bpp);
203 prop_dictionary_set_uint16(dict, "linebytes",
204 roundup2((sizes->surface_width * howmany(sizes->surface_bpp, 8)),
205 64));
206 prop_dictionary_set_uint32(dict, "address", 0); /* XXX >32-bit */
207 CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
208 prop_dictionary_set_uint64(dict, "virtual_address",
209 (uint64_t)(uintptr_t)bus_space_vaddr(sc->sc_ifa.ifa_fb_bst,
210 sc->sc_fb_bsh));
211
212 prop_dictionary_set_uint64(dict, "mode_callback",
213 (uint64_t)(uintptr_t)&intelfb_genfb_mode_callback);
214
215 /* XXX Whattakludge! */
216 #if NVGA > 0
217 if (vga_is_console(dev->pdev->pd_pa.pa_iot, -1)) {
218 what_was_cons = CONS_VGA;
219 prop_dictionary_set_bool(dict, "is_console", true);
220 i915_disable_vga(dev);
221 vga_cndetach();
222 } else
223 #endif
224 if (genfb_is_console() && genfb_is_enabled()) {
225 what_was_cons = CONS_GENFB;
226 prop_dictionary_set_bool(dict, "is_console", true);
227 } else {
228 what_was_cons = CONS_NONE;
229 prop_dictionary_set_bool(dict, "is_console", false);
230 }
231
232 sc->sc_genfb.sc_dev = sc->sc_dev;
233 genfb_init(&sc->sc_genfb);
234 genfb_ops.genfb_ioctl = intelfb_genfb_ioctl;
235 genfb_ops.genfb_mmap = intelfb_genfb_mmap;
236 genfb_ops.genfb_enable_polling = intelfb_genfb_enable_polling;
237 genfb_ops.genfb_disable_polling = intelfb_genfb_disable_polling;
238
239 error = genfb_attach(&sc->sc_genfb, &genfb_ops);
240 if (error) {
241 aprint_error_dev(sc->sc_dev, "failed to attach genfb: %d\n",
242 error);
243 goto fail0;
244 }
245 sc->sc_attached = true;
246
247 drm_fb_helper_set_config(sc->sc_ifa.ifa_fb_helper);
248
249 /* Success! */
250 sc->sc_scheduled = false;
251 return;
252
253 fail0: /* XXX Restore console... */
254 switch (what_was_cons) {
255 case CONS_VGA:
256 break;
257 case CONS_GENFB:
258 break;
259 case CONS_NONE:
260 break;
261 default:
262 break;
263 }
264 }
265
266 static int
267 intelfb_genfb_dpms(struct drm_device *dev, int dpms_mode)
268 {
269 struct drm_i915_private *const dev_priv = dev->dev_private;
270 /* XXX What guarantees dev_priv->fbdev stays around? */
271 struct drm_fb_helper *const fb_helper = &dev_priv->fbdev->helper;
272 unsigned i;
273
274 drm_modeset_lock_all(dev);
275 for (i = 0; i < fb_helper->connector_count; i++) {
276 struct drm_connector *const connector =
277 fb_helper->connector_info[i]->connector;
278 (*connector->funcs->dpms)(connector, dpms_mode);
279 drm_object_property_set_value(&connector->base,
280 dev->mode_config.dpms_property, dpms_mode);
281 }
282 drm_modeset_unlock_all(dev);
283
284 return 0;
285 }
286
287 static int
288 intelfb_genfb_ioctl(void *v, void *vs, unsigned long cmd, void *data, int flag,
289 struct lwp *l)
290 {
291 struct genfb_softc *const genfb = v;
292 struct intelfb_softc *const sc = container_of(genfb,
293 struct intelfb_softc, sc_genfb);
294 struct drm_device *const dev = sc->sc_ifa.ifa_fb_helper->dev;
295 const struct pci_attach_args *const pa = &dev->pdev->pd_pa;
296
297 switch (cmd) {
298 case WSDISPLAYIO_GTYPE:
299 *(unsigned int *)data = WSDISPLAY_TYPE_PCIVGA;
300 return 0;
301
302 /* PCI config read/write passthrough. */
303 case PCI_IOC_CFGREAD:
304 case PCI_IOC_CFGWRITE:
305 return pci_devioctl(pa->pa_pc, pa->pa_tag, cmd, data, flag, l);
306
307 case WSDISPLAYIO_GET_BUSID:
308 return wsdisplayio_busid_pci(dev->dev, pa->pa_pc, pa->pa_tag,
309 data);
310
311 /*
312 * Screen blanking ioctls. Not to be confused with backlight
313 * (can be disabled while stuff is still drawn on the screen),
314 * brightness, or contrast (which we don't support). Backlight
315 * and brightness are done through WSDISPLAYIO_{GET,SET}PARAM.
316 * This toggles between DPMS ON and DPMS OFF; backlight toggles
317 * between DPMS ON and DPMS SUSPEND.
318 */
319 case WSDISPLAYIO_GVIDEO: {
320 int *onp = (int *)data;
321
322 /* XXX Can't really determine a single answer here. */
323 *onp = 1;
324 return 0;
325 }
326
327 case WSDISPLAYIO_SVIDEO: {
328 const int on = *(const int *)data;
329
330 return intelfb_genfb_dpms(dev,
331 on? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF);
332 }
333
334 default:
335 return EPASSTHROUGH;
336 }
337 }
338
339 static paddr_t
340 intelfb_genfb_mmap(void *v, void *vs, off_t offset, int prot)
341 {
342 struct genfb_softc *const genfb = v;
343 struct intelfb_softc *const sc = container_of(genfb,
344 struct intelfb_softc, sc_genfb);
345 struct drm_fb_helper *const helper = sc->sc_ifa.ifa_fb_helper;
346 struct intel_fbdev *const fbdev = container_of(helper,
347 struct intel_fbdev, helper);
348 struct drm_device *const dev = helper->dev;
349 struct drm_i915_private *const dev_priv = dev->dev_private;
350 const struct pci_attach_args *const pa = &dev->pdev->pd_pa;
351 unsigned int i;
352
353 if (offset < 0)
354 return -1;
355
356 /* Treat low memory as the framebuffer itself. */
357 if (offset < genfb->sc_fbsize)
358 return bus_space_mmap(dev->bst,
359 (dev_priv->gtt.mappable_base +
360 i915_gem_obj_ggtt_offset(fbdev->fb->obj)),
361 offset, prot,
362 (BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE));
363
364 /* XXX Cargo-culted from genfb_pci. */
365 if (kauth_authorize_machdep(kauth_cred_get(),
366 KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) != 0) {
367 aprint_normal_dev(dev->dev, "mmap at %"PRIxMAX" rejected\n",
368 (uintmax_t)offset);
369 return -1;
370 }
371
372 for (i = 0; PCI_BAR(i) <= PCI_MAPREG_ROM; i++) {
373 pcireg_t type;
374 bus_addr_t addr;
375 bus_size_t size;
376 int flags;
377
378 /* Interrogate the BAR. */
379 if (!pci_mapreg_probe(pa->pa_pc, pa->pa_tag, PCI_BAR(i),
380 &type))
381 continue;
382 if (PCI_MAPREG_TYPE(type) != PCI_MAPREG_TYPE_MEM)
383 continue;
384 if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, PCI_BAR(i), type,
385 &addr, &size, &flags))
386 continue;
387
388 /* Try to map it if it's in range. */
389 if ((addr <= offset) && (offset < (addr + size)))
390 return bus_space_mmap(pa->pa_memt, addr,
391 (offset - addr), prot, flags);
392
393 /* Skip a slot if this was a 64-bit BAR. */
394 if ((PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_MEM) &&
395 (PCI_MAPREG_MEM_TYPE(type) == PCI_MAPREG_MEM_TYPE_64BIT))
396 i += 1;
397 }
398
399 /* Failure! */
400 return -1;
401 }
402
403 static int
404 intelfb_genfb_enable_polling(void *cookie)
405 {
406 struct genfb_softc *const genfb = cookie;
407 struct intelfb_softc *const sc = container_of(genfb,
408 struct intelfb_softc, sc_genfb);
409
410 return drm_fb_helper_debug_enter_fb(sc->sc_ifa.ifa_fb_helper);
411 }
412
413 static int
414 intelfb_genfb_disable_polling(void *cookie)
415 {
416 struct genfb_softc *const genfb = cookie;
417 struct intelfb_softc *const sc = container_of(genfb,
418 struct intelfb_softc, sc_genfb);
419
420 return drm_fb_helper_debug_leave_fb(sc->sc_ifa.ifa_fb_helper);
421 }
422
423 static bool
424 intelfb_genfb_setmode(struct genfb_softc *genfb, int mode)
425 {
426 struct intelfb_softc *sc = (struct intelfb_softc *)genfb;
427
428 if (mode == WSDISPLAYIO_MODE_EMUL) {
429 drm_fb_helper_set_config(sc->sc_ifa.ifa_fb_helper);
430 }
431
432 return true;
433 }
434