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