xref: /netbsd-src/sys/external/bsd/drm2/drm/drmfb.c (revision 2e2322c9c07009df921d11b1268f8506affbb8ba)
1 /*	$NetBSD: drmfb.c,v 1.3 2016/12/12 19:45:56 maya 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  * drmfb: wsdisplay support, via genfb, for any drm device.  Use this
34  * if you're too lazy to write a hardware-accelerated framebuffer using
35  * wsdisplay directly.
36  *
37  * This doesn't actually do anything interesting -- just hooks up
38  * drmkms crap and genfb crap.
39  */
40 
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: drmfb.c,v 1.3 2016/12/12 19:45:56 maya Exp $");
43 
44 #ifdef _KERNEL_OPT
45 #include "vga.h"
46 #endif
47 
48 #include <sys/types.h>
49 #include <sys/param.h>
50 #include <sys/bus.h>
51 #include <sys/device.h>
52 #include <sys/kauth.h>
53 
54 #if NVGA > 0
55 /*
56  * XXX All we really need is vga_is_console from vgavar.h, but the
57  * header files are missing their own dependencies, so we need to
58  * explicitly drag in the other crap.
59  */
60 #include <dev/ic/mc6845reg.h>
61 #include <dev/ic/pcdisplayvar.h>
62 #include <dev/ic/vgareg.h>
63 #include <dev/ic/vgavar.h>
64 #endif
65 
66 #include <dev/wsfb/genfbvar.h>
67 
68 #include <drm/drmP.h>
69 #include <drm/drm_fb_helper.h>
70 #include <drm/drmfb.h>
71 
72 static int	drmfb_genfb_ioctl(void *, void *, unsigned long, void *, int,
73 		    struct lwp *);
74 static paddr_t	drmfb_genfb_mmap(void *, void *, off_t, int);
75 static int	drmfb_genfb_enable_polling(void *);
76 static int	drmfb_genfb_disable_polling(void *);
77 static bool	drmfb_genfb_setmode(struct genfb_softc *, int);
78 
79 static const struct genfb_mode_callback drmfb_genfb_mode_callback = {
80 	.gmc_setmode = drmfb_genfb_setmode,
81 };
82 
83 int
84 drmfb_attach(struct drmfb_softc *sc, const struct drmfb_attach_args *da)
85 {
86 	const struct drm_fb_helper_surface_size *const sizes = da->da_fb_sizes;
87 	const prop_dictionary_t dict = device_properties(da->da_dev);
88 #if NVGA > 0
89 	struct drm_device *const dev = da->da_fb_helper->dev;
90 #endif
91 	static const struct genfb_ops zero_genfb_ops;
92 	struct genfb_ops genfb_ops = zero_genfb_ops;
93 	enum { CONS_VGA, CONS_GENFB, CONS_NONE } what_was_cons;
94 	bool is_console;
95 	int error;
96 
97 	/* genfb requires this.  */
98 	KASSERTMSG((void *)&sc->sc_genfb == device_private(da->da_dev),
99 	    "drmfb_softc must be first member of device softc");
100 
101 	sc->sc_da = *da;
102 
103 	prop_dictionary_set_uint32(dict, "width", sizes->surface_width);
104 	prop_dictionary_set_uint32(dict, "height", sizes->surface_height);
105 	prop_dictionary_set_uint8(dict, "depth", sizes->surface_bpp);
106 	prop_dictionary_set_uint16(dict, "linebytes", da->da_fb_linebytes);
107 	prop_dictionary_set_uint32(dict, "address", 0); /* XXX >32-bit */
108 	CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
109 	prop_dictionary_set_uint64(dict, "virtual_address",
110 	    (uint64_t)(uintptr_t)da->da_fb_vaddr);
111 
112 	prop_dictionary_set_uint64(dict, "mode_callback",
113 	    (uint64_t)(uintptr_t)&drmfb_genfb_mode_callback);
114 
115 	if (!prop_dictionary_get_bool(dict, "is_console", &is_console)) {
116 		/* XXX Whattakludge!  */
117 #if NVGA > 0
118 		if ((da->da_params->dp_is_vga_console != NULL) &&
119 		    (*da->da_params->dp_is_vga_console)(dev)) {
120 			what_was_cons = CONS_VGA;
121 			prop_dictionary_set_bool(dict, "is_console", true);
122 			vga_cndetach();
123 			if (da->da_params->dp_disable_vga)
124 				(*da->da_params->dp_disable_vga)(dev);
125 		} else
126 #endif
127 		if (genfb_is_console() && genfb_is_enabled()) {
128 			what_was_cons = CONS_GENFB;
129 			prop_dictionary_set_bool(dict, "is_console", true);
130 		} else {
131 			what_was_cons = CONS_NONE;
132 			prop_dictionary_set_bool(dict, "is_console", false);
133 		}
134 	} else {
135 		what_was_cons = CONS_NONE;
136 	}
137 
138 	sc->sc_genfb.sc_dev = sc->sc_da.da_dev;
139 	genfb_init(&sc->sc_genfb);
140 	genfb_ops.genfb_ioctl = drmfb_genfb_ioctl;
141 	genfb_ops.genfb_mmap = drmfb_genfb_mmap;
142 	genfb_ops.genfb_enable_polling = drmfb_genfb_enable_polling;
143 	genfb_ops.genfb_disable_polling = drmfb_genfb_disable_polling;
144 
145 	error = genfb_attach(&sc->sc_genfb, &genfb_ops);
146 	if (error) {
147 		aprint_error_dev(sc->sc_da.da_dev,
148 		    "failed to attach genfb: %d\n", error);
149 		goto fail0;
150 	}
151 
152 	/* Success!  */
153 	return 0;
154 
155 fail0:	KASSERT(error);
156 	/* XXX Restore console...  */
157 	switch (what_was_cons) {
158 	case CONS_VGA:
159 		break;
160 	case CONS_GENFB:
161 		break;
162 	case CONS_NONE:
163 		break;
164 	default:
165 		break;
166 	}
167 	return error;
168 }
169 
170 int
171 drmfb_detach(struct drmfb_softc *sc, int flags)
172 {
173 
174 	/* XXX genfb detach?  */
175 	return 0;
176 }
177 
178 static int
179 drmfb_genfb_ioctl(void *v, void *vs, unsigned long cmd, void *data, int flag,
180     struct lwp *l)
181 {
182 	struct genfb_softc *const genfb = v;
183 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
184 	    sc_genfb);
185 	int error;
186 
187 	if (sc->sc_da.da_params->dp_ioctl) {
188 		error = (*sc->sc_da.da_params->dp_ioctl)(sc, cmd, data, flag,
189 		    l);
190 		if (error != EPASSTHROUGH)
191 			return error;
192 	}
193 
194 	switch (cmd) {
195 	/*
196 	 * Screen blanking ioctls.  Not to be confused with backlight
197 	 * (can be disabled while stuff is still drawn on the screen),
198 	 * brightness, or contrast (which we don't support).  Backlight
199 	 * and brightness are done through WSDISPLAYIO_{GET,SET}PARAM.
200 	 * This toggles between DPMS ON and DPMS OFF; backlight toggles
201 	 * between DPMS ON and DPMS SUSPEND.
202 	 */
203 	case WSDISPLAYIO_GVIDEO: {
204 		int *onp = (int *)data;
205 
206 		/* XXX Can't really determine a single answer here.  */
207 		*onp = 1;
208 		return 0;
209 	}
210 	case WSDISPLAYIO_SVIDEO: {
211 		const int on = *(const int *)data;
212 		const int dpms_mode = on? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
213 		struct drm_fb_helper *const fb_helper = sc->sc_da.da_fb_helper;
214 		struct drm_device *const dev = fb_helper->dev;
215 		unsigned i;
216 
217 		drm_modeset_lock_all(dev);
218 		for (i = 0; i < fb_helper->connector_count; i++) {
219 			struct drm_connector *const connector =
220 			    fb_helper->connector_info[i]->connector;
221 			(*connector->funcs->dpms)(connector, dpms_mode);
222 			drm_object_property_set_value(&connector->base,
223 			    dev->mode_config.dpms_property, dpms_mode);
224 		}
225 		drm_modeset_unlock_all(dev);
226 
227 		return 0;
228 	}
229 	default:
230 		return EPASSTHROUGH;
231 	}
232 }
233 
234 static paddr_t
235 drmfb_genfb_mmap(void *v, void *vs, off_t offset, int prot)
236 {
237 	struct genfb_softc *const genfb = v;
238 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
239 	    sc_genfb);
240 
241 	KASSERT(0 <= offset);
242 
243 	if (offset < genfb->sc_fbsize) {
244 		if (sc->sc_da.da_params->dp_mmapfb == NULL)
245 			return -1;
246 		return (*sc->sc_da.da_params->dp_mmapfb)(sc, offset, prot);
247 	} else {
248 		if (kauth_authorize_machdep(kauth_cred_get(),
249 			KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL)
250 		    != 0)
251 			return -1;
252 		if (sc->sc_da.da_params->dp_mmap == NULL)
253 			return -1;
254 		return (*sc->sc_da.da_params->dp_mmap)(sc, offset, prot);
255 	}
256 }
257 
258 static int
259 drmfb_genfb_enable_polling(void *cookie)
260 {
261 	struct genfb_softc *const genfb = cookie;
262 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
263 	    sc_genfb);
264 
265 	return drm_fb_helper_debug_enter_fb(sc->sc_da.da_fb_helper);
266 }
267 
268 static int
269 drmfb_genfb_disable_polling(void *cookie)
270 {
271 	struct genfb_softc *const genfb = cookie;
272 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
273 	    sc_genfb);
274 
275 	return drm_fb_helper_debug_leave_fb(sc->sc_da.da_fb_helper);
276 }
277 
278 static bool
279 drmfb_genfb_setmode(struct genfb_softc *genfb, int mode)
280 {
281 	struct drmfb_softc *sc = container_of(genfb, struct drmfb_softc,
282 	    sc_genfb);
283 
284 	if (mode == WSDISPLAYIO_MODE_EMUL)
285 		drm_fb_helper_set_config(sc->sc_da.da_fb_helper);
286 
287 	return true;
288 }
289 
290 bool
291 drmfb_shutdown(struct drmfb_softc *sc, int flags __unused)
292 {
293 
294 	genfb_enable_polling(sc->sc_da.da_dev);
295 	return true;
296 }
297