xref: /netbsd-src/sys/external/bsd/drm2/drm/drmfb.c (revision 6ca9ac72e432c1d44a9bc2453510ebe3b8508536)
1 /*	$NetBSD: drmfb.c,v 1.16 2022/09/01 17:54:47 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 /*
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.16 2022/09/01 17:54:47 riastradh 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/drm_device.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
drmfb_attach(struct drmfb_softc * sc,const struct drmfb_attach_args * da)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 	struct drm_connector_list_iter conn_iter;
88 	struct drm_connector *connector;
89 	const prop_dictionary_t dict = device_properties(da->da_dev);
90 	const device_t parent = device_parent(da->da_dev);
91 	const prop_dictionary_t pdict = device_properties(parent);
92 #if NVGA > 0
93 	struct drm_device *const dev = da->da_fb_helper->dev;
94 #endif
95 	static const struct genfb_ops zero_genfb_ops;
96 	struct genfb_ops genfb_ops = zero_genfb_ops;
97 	bool is_console;
98 	int error __diagused;
99 
100 	/* genfb requires this.  */
101 	KASSERTMSG((void *)&sc->sc_genfb == device_private(da->da_dev),
102 	    "drmfb_softc must be first member of device softc");
103 
104 	sc->sc_da = *da;
105 
106 	prop_dictionary_set_uint32(dict, "width", sizes->surface_width);
107 	prop_dictionary_set_uint32(dict, "height", sizes->surface_height);
108 	prop_dictionary_set_uint8(dict, "depth", sizes->surface_bpp);
109 	prop_dictionary_set_uint16(dict, "linebytes", da->da_fb_linebytes);
110 	prop_dictionary_set_uint32(dict, "address", 0); /* XXX >32-bit */
111 	CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
112 	prop_dictionary_set_uint64(dict, "virtual_address",
113 	    (uint64_t)(uintptr_t)da->da_fb_vaddr);
114 
115 	prop_dictionary_set_uint64(dict, "mode_callback",
116 	    (uint64_t)(uintptr_t)&drmfb_genfb_mode_callback);
117 
118 	/*
119 	 * Determine whether MD firmware logic has set the console to
120 	 * go through this device.
121 	 */
122 	if (prop_dictionary_get_bool(pdict, "is_console", &is_console)) {
123 		/* nothing */
124 	} else if (genfb_is_console() && genfb_is_enabled()) {
125 		is_console = true;
126 	} else {
127 		is_console = false;
128 	}
129 
130 #if NVGA > 0
131 	/*
132 	 * Whether or not we were told to be the console, if the
133 	 * console was configured to go through a vga resource that we
134 	 * now own and that vga(4) is not going to take over, kick out
135 	 * the vga console before we take over as genfb console.
136 	 */
137 	if ((da->da_params->dp_is_vga_console != NULL) &&
138 	    (*da->da_params->dp_is_vga_console)(dev)) {
139 		vga_cndetach();
140 		if (da->da_params->dp_disable_vga)
141 			(*da->da_params->dp_disable_vga)(dev);
142 		is_console = true;
143 	}
144 #endif
145 
146 	prop_dictionary_set_bool(dict, "is_console", is_console);
147 
148 	/* Make the first EDID we find available to wsfb */
149 	drm_connector_list_iter_begin(da->da_fb_helper->dev, &conn_iter);
150 	drm_client_for_each_connector_iter(connector, &conn_iter) {
151 		struct drm_property_blob *edid = connector->edid_blob_ptr;
152 		if (edid && edid->length) {
153 			prop_dictionary_set_data(dict, "EDID", edid->data,
154 			    edid->length);
155 			break;
156 		}
157 	}
158 	drm_connector_list_iter_end(&conn_iter);
159 
160 	sc->sc_genfb.sc_dev = sc->sc_da.da_dev;
161 	genfb_init(&sc->sc_genfb);
162 	genfb_ops.genfb_ioctl = drmfb_genfb_ioctl;
163 	genfb_ops.genfb_mmap = drmfb_genfb_mmap;
164 	genfb_ops.genfb_enable_polling = drmfb_genfb_enable_polling;
165 	genfb_ops.genfb_disable_polling = drmfb_genfb_disable_polling;
166 
167 	KERNEL_LOCK(1, NULL);
168 	error = genfb_attach(&sc->sc_genfb, &genfb_ops);
169 	KERNEL_UNLOCK_ONE(NULL);
170 	KASSERTMSG(error == 0, "genfb_attach failed, error=%d", error);
171 
172 	/* Success!  */
173 	return 0;
174 }
175 
176 int
drmfb_detach(struct drmfb_softc * sc,int flags)177 drmfb_detach(struct drmfb_softc *sc, int flags)
178 {
179 
180 	/* XXX genfb detach?  */
181 	return 0;
182 }
183 
184 static int
drmfb_genfb_ioctl(void * v,void * vs,unsigned long cmd,void * data,int flag,struct lwp * l)185 drmfb_genfb_ioctl(void *v, void *vs, unsigned long cmd, void *data, int flag,
186     struct lwp *l)
187 {
188 	struct genfb_softc *const genfb = v;
189 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
190 	    sc_genfb);
191 	int error;
192 
193 	if (sc->sc_da.da_params->dp_ioctl) {
194 		error = (*sc->sc_da.da_params->dp_ioctl)(sc, cmd, data, flag,
195 		    l);
196 		if (error != EPASSTHROUGH)
197 			return error;
198 	}
199 
200 	switch (cmd) {
201 	/*
202 	 * Screen blanking ioctls.  Not to be confused with backlight
203 	 * (can be disabled while stuff is still drawn on the screen),
204 	 * brightness, or contrast (which we don't support).  Backlight
205 	 * and brightness are done through WSDISPLAYIO_{GET,SET}PARAM.
206 	 * This toggles between DPMS ON and DPMS OFF; backlight toggles
207 	 * between DPMS ON and DPMS SUSPEND.
208 	 */
209 	case WSDISPLAYIO_GVIDEO: {
210 		int *onp = (int *)data;
211 
212 		/* XXX Can't really determine a single answer here.  */
213 		*onp = 1;
214 		return 0;
215 	}
216 	case WSDISPLAYIO_SVIDEO: {
217 		const int on = *(const int *)data;
218 		const int dpms_mode = on? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
219 		struct drm_fb_helper *const fb_helper = sc->sc_da.da_fb_helper;
220 
221 		mutex_lock(&fb_helper->lock);
222 		drm_client_modeset_dpms(&fb_helper->client, dpms_mode);
223 		mutex_unlock(&fb_helper->lock);
224 
225 		return 0;
226 	}
227 	default:
228 		return EPASSTHROUGH;
229 	}
230 }
231 
232 static paddr_t
drmfb_genfb_mmap(void * v,void * vs,off_t offset,int prot)233 drmfb_genfb_mmap(void *v, void *vs, off_t offset, int prot)
234 {
235 	struct genfb_softc *const genfb = v;
236 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
237 	    sc_genfb);
238 
239 	KASSERT(0 <= offset);
240 
241 	if (offset < genfb->sc_fbsize) {
242 		if (sc->sc_da.da_params->dp_mmapfb == NULL)
243 			return -1;
244 		return (*sc->sc_da.da_params->dp_mmapfb)(sc, offset, prot);
245 	} else {
246 		if (kauth_authorize_machdep(kauth_cred_get(),
247 			KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL)
248 		    != 0)
249 			return -1;
250 		if (sc->sc_da.da_params->dp_mmap == NULL)
251 			return -1;
252 		return (*sc->sc_da.da_params->dp_mmap)(sc, offset, prot);
253 	}
254 }
255 
256 static int
drmfb_genfb_enable_polling(void * cookie)257 drmfb_genfb_enable_polling(void *cookie)
258 {
259 	struct genfb_softc *const genfb = cookie;
260 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
261 	    sc_genfb);
262 
263 	return drm_fb_helper_debug_enter_fb(sc->sc_da.da_fb_helper);
264 }
265 
266 static int
drmfb_genfb_disable_polling(void * cookie)267 drmfb_genfb_disable_polling(void *cookie)
268 {
269 	struct genfb_softc *const genfb = cookie;
270 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
271 	    sc_genfb);
272 
273 	return drm_fb_helper_debug_leave_fb(sc->sc_da.da_fb_helper);
274 }
275 
276 static bool
drmfb_genfb_setmode(struct genfb_softc * genfb,int mode)277 drmfb_genfb_setmode(struct genfb_softc *genfb, int mode)
278 {
279 	struct drmfb_softc *sc = container_of(genfb, struct drmfb_softc,
280 	    sc_genfb);
281 	struct drm_fb_helper *fb_helper = sc->sc_da.da_fb_helper;
282 
283 	if (mode == WSDISPLAYIO_MODE_EMUL)
284 		drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
285 
286 	return true;
287 }
288 
289 bool
drmfb_shutdown(struct drmfb_softc * sc,int flags __unused)290 drmfb_shutdown(struct drmfb_softc *sc, int flags __unused)
291 {
292 
293 	genfb_enable_polling(sc->sc_da.da_dev);
294 	return true;
295 }
296