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