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