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