1 | /* $NetBSD: drmfb.c,v 1.2 2015/11/09 23:11:18 jmcneill 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.2 2015/11/09 23:11:18 jmcneill 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" , |
107 | roundup2((sizes->surface_width * howmany(sizes->surface_bpp, 8)), |
108 | 64)); |
109 | prop_dictionary_set_uint32(dict, "address" , 0); /* XXX >32-bit */ |
110 | CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t)); |
111 | prop_dictionary_set_uint64(dict, "virtual_address" , |
112 | (uint64_t)(uintptr_t)da->da_fb_vaddr); |
113 | |
114 | prop_dictionary_set_uint64(dict, "mode_callback" , |
115 | (uint64_t)(uintptr_t)&drmfb_genfb_mode_callback); |
116 | |
117 | if (!prop_dictionary_get_bool(dict, "is_console" , &is_console)) { |
118 | /* XXX Whattakludge! */ |
119 | #if NVGA > 0 |
120 | if ((da->da_params->dp_is_vga_console != NULL) && |
121 | (*da->da_params->dp_is_vga_console)(dev)) { |
122 | what_was_cons = CONS_VGA; |
123 | prop_dictionary_set_bool(dict, "is_console" , true); |
124 | vga_cndetach(); |
125 | if (da->da_params->dp_disable_vga) |
126 | (*da->da_params->dp_disable_vga)(dev); |
127 | } else |
128 | #endif |
129 | if (genfb_is_console() && genfb_is_enabled()) { |
130 | what_was_cons = CONS_GENFB; |
131 | prop_dictionary_set_bool(dict, "is_console" , true); |
132 | } else { |
133 | what_was_cons = CONS_NONE; |
134 | prop_dictionary_set_bool(dict, "is_console" , false); |
135 | } |
136 | } else { |
137 | what_was_cons = CONS_NONE; |
138 | } |
139 | |
140 | sc->sc_genfb.sc_dev = sc->sc_da.da_dev; |
141 | genfb_init(&sc->sc_genfb); |
142 | genfb_ops.genfb_ioctl = drmfb_genfb_ioctl; |
143 | genfb_ops.genfb_mmap = drmfb_genfb_mmap; |
144 | genfb_ops.genfb_enable_polling = drmfb_genfb_enable_polling; |
145 | genfb_ops.genfb_disable_polling = drmfb_genfb_disable_polling; |
146 | |
147 | error = genfb_attach(&sc->sc_genfb, &genfb_ops); |
148 | if (error) { |
149 | aprint_error_dev(sc->sc_da.da_dev, |
150 | "failed to attach genfb: %d\n" , error); |
151 | goto fail0; |
152 | } |
153 | |
154 | /* Success! */ |
155 | return 0; |
156 | |
157 | fail0: KASSERT(error); |
158 | /* XXX Restore console... */ |
159 | switch (what_was_cons) { |
160 | case CONS_VGA: |
161 | break; |
162 | case CONS_GENFB: |
163 | break; |
164 | case CONS_NONE: |
165 | break; |
166 | default: |
167 | break; |
168 | } |
169 | return error; |
170 | } |
171 | |
172 | int |
173 | drmfb_detach(struct drmfb_softc *sc, int flags) |
174 | { |
175 | |
176 | /* XXX genfb detach? */ |
177 | return 0; |
178 | } |
179 | |
180 | static int |
181 | drmfb_genfb_ioctl(void *v, void *vs, unsigned long cmd, void *data, int flag, |
182 | struct lwp *l) |
183 | { |
184 | struct genfb_softc *const genfb = v; |
185 | struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc, |
186 | sc_genfb); |
187 | int error; |
188 | |
189 | if (sc->sc_da.da_params->dp_ioctl) { |
190 | error = (*sc->sc_da.da_params->dp_ioctl)(sc, cmd, data, flag, |
191 | l); |
192 | if (error != EPASSTHROUGH) |
193 | return error; |
194 | } |
195 | |
196 | switch (cmd) { |
197 | /* |
198 | * Screen blanking ioctls. Not to be confused with backlight |
199 | * (can be disabled while stuff is still drawn on the screen), |
200 | * brightness, or contrast (which we don't support). Backlight |
201 | * and brightness are done through WSDISPLAYIO_{GET,SET}PARAM. |
202 | * This toggles between DPMS ON and DPMS OFF; backlight toggles |
203 | * between DPMS ON and DPMS SUSPEND. |
204 | */ |
205 | case WSDISPLAYIO_GVIDEO: { |
206 | int *onp = (int *)data; |
207 | |
208 | /* XXX Can't really determine a single answer here. */ |
209 | *onp = 1; |
210 | return 0; |
211 | } |
212 | case WSDISPLAYIO_SVIDEO: { |
213 | const int on = *(const int *)data; |
214 | const int dpms_mode = on? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF; |
215 | struct drm_fb_helper *const fb_helper = sc->sc_da.da_fb_helper; |
216 | struct drm_device *const dev = fb_helper->dev; |
217 | unsigned i; |
218 | |
219 | drm_modeset_lock_all(dev); |
220 | for (i = 0; i < fb_helper->connector_count; i++) { |
221 | struct drm_connector *const connector = |
222 | fb_helper->connector_info[i]->connector; |
223 | (*connector->funcs->dpms)(connector, dpms_mode); |
224 | drm_object_property_set_value(&connector->base, |
225 | dev->mode_config.dpms_property, dpms_mode); |
226 | } |
227 | drm_modeset_unlock_all(dev); |
228 | |
229 | return 0; |
230 | } |
231 | default: |
232 | return EPASSTHROUGH; |
233 | } |
234 | } |
235 | |
236 | static paddr_t |
237 | drmfb_genfb_mmap(void *v, void *vs, off_t offset, int prot) |
238 | { |
239 | struct genfb_softc *const genfb = v; |
240 | struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc, |
241 | sc_genfb); |
242 | |
243 | KASSERT(0 <= offset); |
244 | |
245 | if (offset < genfb->sc_fbsize) { |
246 | if (sc->sc_da.da_params->dp_mmapfb == NULL) |
247 | return -1; |
248 | return (*sc->sc_da.da_params->dp_mmapfb)(sc, offset, prot); |
249 | } else { |
250 | if (kauth_authorize_machdep(kauth_cred_get(), |
251 | KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) |
252 | != 0) |
253 | return -1; |
254 | if (sc->sc_da.da_params->dp_mmap == NULL) |
255 | return -1; |
256 | return (*sc->sc_da.da_params->dp_mmap)(sc, offset, prot); |
257 | } |
258 | } |
259 | |
260 | static int |
261 | drmfb_genfb_enable_polling(void *cookie) |
262 | { |
263 | struct genfb_softc *const genfb = cookie; |
264 | struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc, |
265 | sc_genfb); |
266 | |
267 | return drm_fb_helper_debug_enter_fb(sc->sc_da.da_fb_helper); |
268 | } |
269 | |
270 | static int |
271 | drmfb_genfb_disable_polling(void *cookie) |
272 | { |
273 | struct genfb_softc *const genfb = cookie; |
274 | struct drmfb_softc *const sc = container_of(genfb, struct drmfb_softc, |
275 | sc_genfb); |
276 | |
277 | return drm_fb_helper_debug_leave_fb(sc->sc_da.da_fb_helper); |
278 | } |
279 | |
280 | static bool |
281 | drmfb_genfb_setmode(struct genfb_softc *genfb, int mode) |
282 | { |
283 | struct drmfb_softc *sc = container_of(genfb, struct drmfb_softc, |
284 | sc_genfb); |
285 | |
286 | if (mode == WSDISPLAYIO_MODE_EMUL) |
287 | drm_fb_helper_set_config(sc->sc_da.da_fb_helper); |
288 | |
289 | return true; |
290 | } |
291 | |
292 | bool |
293 | drmfb_shutdown(struct drmfb_softc *sc, int flags __unused) |
294 | { |
295 | |
296 | genfb_enable_polling(sc->sc_da.da_dev); |
297 | return true; |
298 | } |
299 | |