xref: /netbsd-src/sys/external/bsd/drm2/drm/drmfb.c (revision 6ca9ac72e432c1d44a9bc2453510ebe3b8508536)
1*6ca9ac72Sriastradh /*	$NetBSD: drmfb.c,v 1.16 2022/09/01 17:54:47 riastradh Exp $	*/
2894a903bSriastradh 
3894a903bSriastradh /*-
4894a903bSriastradh  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5894a903bSriastradh  * All rights reserved.
6894a903bSriastradh  *
7894a903bSriastradh  * This code is derived from software contributed to The NetBSD Foundation
8894a903bSriastradh  * by Taylor R. Campbell.
9894a903bSriastradh  *
10894a903bSriastradh  * Redistribution and use in source and binary forms, with or without
11894a903bSriastradh  * modification, are permitted provided that the following conditions
12894a903bSriastradh  * are met:
13894a903bSriastradh  * 1. Redistributions of source code must retain the above copyright
14894a903bSriastradh  *    notice, this list of conditions and the following disclaimer.
15894a903bSriastradh  * 2. Redistributions in binary form must reproduce the above copyright
16894a903bSriastradh  *    notice, this list of conditions and the following disclaimer in the
17894a903bSriastradh  *    documentation and/or other materials provided with the distribution.
18894a903bSriastradh  *
19894a903bSriastradh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20894a903bSriastradh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21894a903bSriastradh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22894a903bSriastradh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23894a903bSriastradh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24894a903bSriastradh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25894a903bSriastradh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26894a903bSriastradh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27894a903bSriastradh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28894a903bSriastradh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29894a903bSriastradh  * POSSIBILITY OF SUCH DAMAGE.
30894a903bSriastradh  */
31894a903bSriastradh 
32894a903bSriastradh /*
33894a903bSriastradh  * drmfb: wsdisplay support, via genfb, for any drm device.  Use this
34894a903bSriastradh  * if you're too lazy to write a hardware-accelerated framebuffer using
35894a903bSriastradh  * wsdisplay directly.
36894a903bSriastradh  *
37894a903bSriastradh  * This doesn't actually do anything interesting -- just hooks up
38894a903bSriastradh  * drmkms crap and genfb crap.
39894a903bSriastradh  */
40894a903bSriastradh 
41894a903bSriastradh #include <sys/cdefs.h>
42*6ca9ac72Sriastradh __KERNEL_RCSID(0, "$NetBSD: drmfb.c,v 1.16 2022/09/01 17:54:47 riastradh Exp $");
43894a903bSriastradh 
44894a903bSriastradh #ifdef _KERNEL_OPT
45894a903bSriastradh #include "vga.h"
46894a903bSriastradh #endif
47894a903bSriastradh 
48894a903bSriastradh #include <sys/types.h>
49894a903bSriastradh #include <sys/param.h>
50894a903bSriastradh #include <sys/bus.h>
51894a903bSriastradh #include <sys/device.h>
52894a903bSriastradh #include <sys/kauth.h>
53894a903bSriastradh 
54894a903bSriastradh #if NVGA > 0
55894a903bSriastradh /*
56894a903bSriastradh  * XXX All we really need is vga_is_console from vgavar.h, but the
57894a903bSriastradh  * header files are missing their own dependencies, so we need to
58894a903bSriastradh  * explicitly drag in the other crap.
59894a903bSriastradh  */
60894a903bSriastradh #include <dev/ic/mc6845reg.h>
61894a903bSriastradh #include <dev/ic/pcdisplayvar.h>
62894a903bSriastradh #include <dev/ic/vgareg.h>
63894a903bSriastradh #include <dev/ic/vgavar.h>
64894a903bSriastradh #endif
65894a903bSriastradh 
66894a903bSriastradh #include <dev/wsfb/genfbvar.h>
67894a903bSriastradh 
68f21b21b0Sriastradh #include <drm/drm_device.h>
69894a903bSriastradh #include <drm/drm_fb_helper.h>
70894a903bSriastradh #include <drm/drmfb.h>
71894a903bSriastradh 
72894a903bSriastradh static int	drmfb_genfb_ioctl(void *, void *, unsigned long, void *, int,
73894a903bSriastradh 		    struct lwp *);
74894a903bSriastradh static paddr_t	drmfb_genfb_mmap(void *, void *, off_t, int);
75894a903bSriastradh static int	drmfb_genfb_enable_polling(void *);
76894a903bSriastradh static int	drmfb_genfb_disable_polling(void *);
77894a903bSriastradh static bool	drmfb_genfb_setmode(struct genfb_softc *, int);
78894a903bSriastradh 
79894a903bSriastradh static const struct genfb_mode_callback drmfb_genfb_mode_callback = {
80894a903bSriastradh 	.gmc_setmode = drmfb_genfb_setmode,
81894a903bSriastradh };
82894a903bSriastradh 
83894a903bSriastradh int
drmfb_attach(struct drmfb_softc * sc,const struct drmfb_attach_args * da)84894a903bSriastradh drmfb_attach(struct drmfb_softc *sc, const struct drmfb_attach_args *da)
85894a903bSriastradh {
86894a903bSriastradh 	const struct drm_fb_helper_surface_size *const sizes = da->da_fb_sizes;
8728f7b3e6Sriastradh 	struct drm_connector_list_iter conn_iter;
8828f7b3e6Sriastradh 	struct drm_connector *connector;
89894a903bSriastradh 	const prop_dictionary_t dict = device_properties(da->da_dev);
90092adc4cSriastradh 	const device_t parent = device_parent(da->da_dev);
91092adc4cSriastradh 	const prop_dictionary_t pdict = device_properties(parent);
92894a903bSriastradh #if NVGA > 0
93894a903bSriastradh 	struct drm_device *const dev = da->da_fb_helper->dev;
94894a903bSriastradh #endif
95894a903bSriastradh 	static const struct genfb_ops zero_genfb_ops;
96894a903bSriastradh 	struct genfb_ops genfb_ops = zero_genfb_ops;
971dadc0e1Sjmcneill 	bool is_console;
98*6ca9ac72Sriastradh 	int error __diagused;
99894a903bSriastradh 
100894a903bSriastradh 	/* genfb requires this.  */
101894a903bSriastradh 	KASSERTMSG((void *)&sc->sc_genfb == device_private(da->da_dev),
102894a903bSriastradh 	    "drmfb_softc must be first member of device softc");
103894a903bSriastradh 
104894a903bSriastradh 	sc->sc_da = *da;
105894a903bSriastradh 
106894a903bSriastradh 	prop_dictionary_set_uint32(dict, "width", sizes->surface_width);
107894a903bSriastradh 	prop_dictionary_set_uint32(dict, "height", sizes->surface_height);
108894a903bSriastradh 	prop_dictionary_set_uint8(dict, "depth", sizes->surface_bpp);
1098538f99cSmaya 	prop_dictionary_set_uint16(dict, "linebytes", da->da_fb_linebytes);
110894a903bSriastradh 	prop_dictionary_set_uint32(dict, "address", 0); /* XXX >32-bit */
111894a903bSriastradh 	CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
112894a903bSriastradh 	prop_dictionary_set_uint64(dict, "virtual_address",
113894a903bSriastradh 	    (uint64_t)(uintptr_t)da->da_fb_vaddr);
114894a903bSriastradh 
115894a903bSriastradh 	prop_dictionary_set_uint64(dict, "mode_callback",
116894a903bSriastradh 	    (uint64_t)(uintptr_t)&drmfb_genfb_mode_callback);
117894a903bSriastradh 
118*6ca9ac72Sriastradh 	/*
119*6ca9ac72Sriastradh 	 * Determine whether MD firmware logic has set the console to
120*6ca9ac72Sriastradh 	 * go through this device.
121*6ca9ac72Sriastradh 	 */
122092adc4cSriastradh 	if (prop_dictionary_get_bool(pdict, "is_console", &is_console)) {
123*6ca9ac72Sriastradh 		/* nothing */
124*6ca9ac72Sriastradh 	} else if (genfb_is_console() && genfb_is_enabled()) {
125*6ca9ac72Sriastradh 		is_console = true;
126092adc4cSriastradh 	} else {
127*6ca9ac72Sriastradh 		is_console = false;
128*6ca9ac72Sriastradh 	}
129*6ca9ac72Sriastradh 
130894a903bSriastradh #if NVGA > 0
131*6ca9ac72Sriastradh 	/*
132*6ca9ac72Sriastradh 	 * Whether or not we were told to be the console, if the
133*6ca9ac72Sriastradh 	 * console was configured to go through a vga resource that we
134*6ca9ac72Sriastradh 	 * now own and that vga(4) is not going to take over, kick out
135*6ca9ac72Sriastradh 	 * the vga console before we take over as genfb console.
136*6ca9ac72Sriastradh 	 */
137894a903bSriastradh 	if ((da->da_params->dp_is_vga_console != NULL) &&
138894a903bSriastradh 	    (*da->da_params->dp_is_vga_console)(dev)) {
139894a903bSriastradh 		vga_cndetach();
140894a903bSriastradh 		if (da->da_params->dp_disable_vga)
141894a903bSriastradh 			(*da->da_params->dp_disable_vga)(dev);
142*6ca9ac72Sriastradh 		is_console = true;
143*6ca9ac72Sriastradh 	}
144894a903bSriastradh #endif
145*6ca9ac72Sriastradh 
146*6ca9ac72Sriastradh 	prop_dictionary_set_bool(dict, "is_console", is_console);
147894a903bSriastradh 
1485e72694cSjmcneill 	/* Make the first EDID we find available to wsfb */
14928f7b3e6Sriastradh 	drm_connector_list_iter_begin(da->da_fb_helper->dev, &conn_iter);
15028f7b3e6Sriastradh 	drm_client_for_each_connector_iter(connector, &conn_iter) {
1515e72694cSjmcneill 		struct drm_property_blob *edid = connector->edid_blob_ptr;
15294682a2cSjmcneill 		if (edid && edid->length) {
153e08bb185Sjmcneill 			prop_dictionary_set_data(dict, "EDID", edid->data,
154e08bb185Sjmcneill 			    edid->length);
1555e72694cSjmcneill 			break;
1565e72694cSjmcneill 		}
1575e72694cSjmcneill 	}
15828f7b3e6Sriastradh 	drm_connector_list_iter_end(&conn_iter);
1595e72694cSjmcneill 
160894a903bSriastradh 	sc->sc_genfb.sc_dev = sc->sc_da.da_dev;
161894a903bSriastradh 	genfb_init(&sc->sc_genfb);
162894a903bSriastradh 	genfb_ops.genfb_ioctl = drmfb_genfb_ioctl;
163894a903bSriastradh 	genfb_ops.genfb_mmap = drmfb_genfb_mmap;
164894a903bSriastradh 	genfb_ops.genfb_enable_polling = drmfb_genfb_enable_polling;
165894a903bSriastradh 	genfb_ops.genfb_disable_polling = drmfb_genfb_disable_polling;
166894a903bSriastradh 
1670f625318Schs 	KERNEL_LOCK(1, NULL);
168894a903bSriastradh 	error = genfb_attach(&sc->sc_genfb, &genfb_ops);
1690f625318Schs 	KERNEL_UNLOCK_ONE(NULL);
170*6ca9ac72Sriastradh 	KASSERTMSG(error == 0, "genfb_attach failed, error=%d", error);
171894a903bSriastradh 
172894a903bSriastradh 	/* Success!  */
173894a903bSriastradh 	return 0;
174894a903bSriastradh }
175894a903bSriastradh 
176894a903bSriastradh int
drmfb_detach(struct drmfb_softc * sc,int flags)177894a903bSriastradh drmfb_detach(struct drmfb_softc *sc, int flags)
178894a903bSriastradh {
179894a903bSriastradh 
180894a903bSriastradh 	/* XXX genfb detach?  */
181894a903bSriastradh 	return 0;
182894a903bSriastradh }
183894a903bSriastradh 
184894a903bSriastradh static int
drmfb_genfb_ioctl(void * v,void * vs,unsigned long cmd,void * data,int flag,struct lwp * l)185894a903bSriastradh drmfb_genfb_ioctl(void *v, void *vs, unsigned long cmd, void *data, int flag,
186894a903bSriastradh     struct lwp *l)
187894a903bSriastradh {
188894a903bSriastradh 	struct genfb_softc *const genfb = v;
189894a903bSriastradh 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
190894a903bSriastradh 	    sc_genfb);
191894a903bSriastradh 	int error;
192894a903bSriastradh 
193894a903bSriastradh 	if (sc->sc_da.da_params->dp_ioctl) {
194894a903bSriastradh 		error = (*sc->sc_da.da_params->dp_ioctl)(sc, cmd, data, flag,
195894a903bSriastradh 		    l);
196894a903bSriastradh 		if (error != EPASSTHROUGH)
197894a903bSriastradh 			return error;
198894a903bSriastradh 	}
199894a903bSriastradh 
200894a903bSriastradh 	switch (cmd) {
201894a903bSriastradh 	/*
202894a903bSriastradh 	 * Screen blanking ioctls.  Not to be confused with backlight
203894a903bSriastradh 	 * (can be disabled while stuff is still drawn on the screen),
204894a903bSriastradh 	 * brightness, or contrast (which we don't support).  Backlight
205894a903bSriastradh 	 * and brightness are done through WSDISPLAYIO_{GET,SET}PARAM.
206894a903bSriastradh 	 * This toggles between DPMS ON and DPMS OFF; backlight toggles
207894a903bSriastradh 	 * between DPMS ON and DPMS SUSPEND.
208894a903bSriastradh 	 */
209894a903bSriastradh 	case WSDISPLAYIO_GVIDEO: {
210894a903bSriastradh 		int *onp = (int *)data;
211894a903bSriastradh 
212894a903bSriastradh 		/* XXX Can't really determine a single answer here.  */
213894a903bSriastradh 		*onp = 1;
214894a903bSriastradh 		return 0;
215894a903bSriastradh 	}
216894a903bSriastradh 	case WSDISPLAYIO_SVIDEO: {
217894a903bSriastradh 		const int on = *(const int *)data;
218894a903bSriastradh 		const int dpms_mode = on? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
219894a903bSriastradh 		struct drm_fb_helper *const fb_helper = sc->sc_da.da_fb_helper;
220894a903bSriastradh 
22147edd72aSriastradh 		mutex_lock(&fb_helper->lock);
22247edd72aSriastradh 		drm_client_modeset_dpms(&fb_helper->client, dpms_mode);
22347edd72aSriastradh 		mutex_unlock(&fb_helper->lock);
224894a903bSriastradh 
225894a903bSriastradh 		return 0;
226894a903bSriastradh 	}
227894a903bSriastradh 	default:
228894a903bSriastradh 		return EPASSTHROUGH;
229894a903bSriastradh 	}
230894a903bSriastradh }
231894a903bSriastradh 
232894a903bSriastradh static paddr_t
drmfb_genfb_mmap(void * v,void * vs,off_t offset,int prot)233894a903bSriastradh drmfb_genfb_mmap(void *v, void *vs, off_t offset, int prot)
234894a903bSriastradh {
235894a903bSriastradh 	struct genfb_softc *const genfb = v;
236894a903bSriastradh 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
237894a903bSriastradh 	    sc_genfb);
238894a903bSriastradh 
239894a903bSriastradh 	KASSERT(0 <= offset);
240894a903bSriastradh 
241894a903bSriastradh 	if (offset < genfb->sc_fbsize) {
242894a903bSriastradh 		if (sc->sc_da.da_params->dp_mmapfb == NULL)
243894a903bSriastradh 			return -1;
244894a903bSriastradh 		return (*sc->sc_da.da_params->dp_mmapfb)(sc, offset, prot);
245894a903bSriastradh 	} else {
246894a903bSriastradh 		if (kauth_authorize_machdep(kauth_cred_get(),
247894a903bSriastradh 			KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL)
248894a903bSriastradh 		    != 0)
249894a903bSriastradh 			return -1;
250894a903bSriastradh 		if (sc->sc_da.da_params->dp_mmap == NULL)
251894a903bSriastradh 			return -1;
252894a903bSriastradh 		return (*sc->sc_da.da_params->dp_mmap)(sc, offset, prot);
253894a903bSriastradh 	}
254894a903bSriastradh }
255894a903bSriastradh 
256894a903bSriastradh static int
drmfb_genfb_enable_polling(void * cookie)257894a903bSriastradh drmfb_genfb_enable_polling(void *cookie)
258894a903bSriastradh {
259894a903bSriastradh 	struct genfb_softc *const genfb = cookie;
260894a903bSriastradh 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
261894a903bSriastradh 	    sc_genfb);
262894a903bSriastradh 
263894a903bSriastradh 	return drm_fb_helper_debug_enter_fb(sc->sc_da.da_fb_helper);
264894a903bSriastradh }
265894a903bSriastradh 
266894a903bSriastradh static int
drmfb_genfb_disable_polling(void * cookie)267894a903bSriastradh drmfb_genfb_disable_polling(void *cookie)
268894a903bSriastradh {
269894a903bSriastradh 	struct genfb_softc *const genfb = cookie;
270894a903bSriastradh 	struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc,
271894a903bSriastradh 	    sc_genfb);
272894a903bSriastradh 
273894a903bSriastradh 	return drm_fb_helper_debug_leave_fb(sc->sc_da.da_fb_helper);
274894a903bSriastradh }
275894a903bSriastradh 
276894a903bSriastradh static bool
drmfb_genfb_setmode(struct genfb_softc * genfb,int mode)277894a903bSriastradh drmfb_genfb_setmode(struct genfb_softc *genfb, int mode)
278894a903bSriastradh {
279894a903bSriastradh 	struct drmfb_softc *sc = container_of(genfb, struct drmfb_softc,
280894a903bSriastradh 	    sc_genfb);
281a67520d2Sriastradh 	struct drm_fb_helper *fb_helper = sc->sc_da.da_fb_helper;
282894a903bSriastradh 
283894a903bSriastradh 	if (mode == WSDISPLAYIO_MODE_EMUL)
284a67520d2Sriastradh 		drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
285894a903bSriastradh 
286894a903bSriastradh 	return true;
287894a903bSriastradh }
288894a903bSriastradh 
289894a903bSriastradh bool
drmfb_shutdown(struct drmfb_softc * sc,int flags __unused)290894a903bSriastradh drmfb_shutdown(struct drmfb_softc *sc, int flags __unused)
291894a903bSriastradh {
292894a903bSriastradh 
293894a903bSriastradh 	genfb_enable_polling(sc->sc_da.da_dev);
294894a903bSriastradh 	return true;
295894a903bSriastradh }
296