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