1 /* $NetBSD: genfb.c,v 1.92 2025/04/29 12:20:36 tsutsui Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: genfb.c,v 1.92 2025/04/29 12:20:36 tsutsui Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/device.h> 36 #include <sys/proc.h> 37 #include <sys/mutex.h> 38 #include <sys/ioctl.h> 39 #include <sys/kernel.h> 40 #include <sys/systm.h> 41 #include <sys/kmem.h> 42 #include <sys/reboot.h> 43 44 #include <uvm/uvm_extern.h> 45 46 #include <dev/wscons/wsconsio.h> 47 #include <dev/wscons/wsdisplayvar.h> 48 #include <dev/rasops/rasops.h> 49 #include <dev/wsfont/wsfont.h> 50 51 #include <dev/wscons/wsdisplay_vconsvar.h> 52 53 #include <dev/wsfb/genfbvar.h> 54 55 #include <dev/videomode/videomode.h> 56 #include <dev/videomode/edidvar.h> 57 58 #ifdef GENFB_DISABLE_TEXT 59 #define DISABLESPLASH (boothowto & (RB_SINGLE | RB_USERCONF | RB_ASKNAME | \ 60 AB_VERBOSE | AB_DEBUG) ) 61 #endif 62 63 #ifdef _KERNEL_OPT 64 #include "opt_genfb.h" 65 #include "opt_wsfb.h" 66 #include "opt_rasops.h" 67 #endif 68 69 #ifdef GENFB_DEBUG 70 #define GPRINTF panic 71 #else 72 #define GPRINTF aprint_debug 73 #endif 74 75 #define GENFB_BRIGHTNESS_STEP 15 76 #define GENFB_CHAR_WIDTH_MM 3 77 78 struct genfb_private { 79 struct genfb_ops sc_ops; 80 struct vcons_screen sc_console_screen; 81 struct wsscreen_descr sc_defaultscreen_descr; 82 const struct wsscreen_descr *sc_screens[1]; 83 struct wsscreen_list sc_screenlist; 84 struct genfb_colormap_callback *sc_cmcb; 85 struct genfb_parameter_callback *sc_backlight; 86 struct genfb_parameter_callback *sc_brightness; 87 struct genfb_mode_callback *sc_modecb; 88 uint32_t *sc_devcmap; 89 int sc_backlight_level, sc_backlight_on; 90 void *sc_shadowfb; 91 bool sc_enable_shadowfb; 92 int sc_mode; 93 u_char sc_cmap_red[256]; 94 u_char sc_cmap_green[256]; 95 u_char sc_cmap_blue[256]; 96 bool sc_want_clear; 97 #ifdef SPLASHSCREEN 98 struct splash_info sc_splash; 99 #endif 100 struct wsdisplay_accessops sc_accessops; 101 #if GENFB_GLYPHCACHE > 0 102 /* 103 * The generic glyphcache code makes a bunch of assumptions that are 104 * true for most graphics hardware with a directly supported blitter. 105 * For example it assume that 106 * - VRAM access from the host is expensive 107 * - copying data around in VRAM is cheap and can happen in parallel 108 * to the host CPU 109 * -> therefore we draw glyphs normally if we have to, so the ( assumed 110 * to be hardware assisted ) driver supplied putchar() method doesn't 111 * need to be glyphcache aware, then copy them away for later use 112 * for genfb things are a bit different. On most hardware: 113 * - VRAM access from the host is still expensive 114 * - copying data around in VRAM is also expensive since we don't have 115 * a blitter and VRAM is mapped uncached 116 * - VRAM reads are usually slower than writes ( write combining and 117 * such help writes but not reads, and VRAM might be behind an 118 * asymmetric bus like AGP ) and must be avoided, both are much 119 * slower than main memory 120 * -> therefore we cache glyphs in main memory, no reason to map it 121 * uncached, we draw into the cache first and then copy the glyph 122 * into video memory to avoid framebuffer reads and to allow more 123 * efficient write accesses than putchar() would offer 124 * Because of this we can't use the generic code but we can recycle a 125 * few data structures. 126 */ 127 uint8_t *sc_cache; 128 struct rasops_info sc_cache_ri; 129 void (*sc_putchar)(void *, int, int, u_int, long); 130 int sc_cache_cells; 131 int sc_nbuckets; /* buckets allocated */ 132 gc_bucket *sc_buckets; /* we allocate as many as we can get into ram */ 133 int sc_attrmap[256]; /* mapping a colour attribute to a bucket */ 134 #endif 135 }; 136 137 static int genfb_ioctl(void *, void *, u_long, void *, int, struct lwp *); 138 static paddr_t genfb_mmap(void *, void *, off_t, int); 139 static void genfb_pollc(void *, int); 140 141 static void genfb_init_screen(void *, struct vcons_screen *, int, long *); 142 static int genfb_calc_hsize(struct genfb_softc *); 143 static int genfb_calc_cols(struct genfb_softc *, struct rasops_info *); 144 145 static int genfb_putcmap(struct genfb_softc *, struct wsdisplay_cmap *); 146 static int genfb_getcmap(struct genfb_softc *, struct wsdisplay_cmap *); 147 static int genfb_putpalreg(struct genfb_softc *, uint8_t, uint8_t, 148 uint8_t, uint8_t); 149 static void genfb_init_palette(struct genfb_softc *); 150 151 static void genfb_brightness_up(device_t); 152 static void genfb_brightness_down(device_t); 153 154 #if GENFB_GLYPHCACHE > 0 155 static int genfb_setup_glyphcache(struct genfb_softc *, long); 156 static void genfb_putchar(void *, int, int, u_int, long); 157 #endif 158 159 extern const u_char rasops_cmap[768]; 160 161 static int genfb_cnattach_called = 0; 162 static int genfb_enabled = 1; 163 164 static struct genfb_softc *genfb_softc = NULL; 165 166 void 167 genfb_init(struct genfb_softc *sc) 168 { 169 struct genfb_private *scp; 170 prop_dictionary_t dict; 171 uint64_t cmap_cb, pmf_cb, mode_cb, bl_cb, br_cb, devcmap, fbaddr; 172 uint64_t fboffset; 173 bool console; 174 175 scp = sc->sc_private = kmem_zalloc(sizeof(*sc->sc_private), KM_SLEEP); 176 177 dict = device_properties(sc->sc_dev); 178 #ifdef GENFB_DEBUG 179 printf("%s", prop_dictionary_externalize(dict)); 180 #endif 181 prop_dictionary_get_bool(dict, "is_console", &console); 182 183 if (!prop_dictionary_get_uint32(dict, "width", &sc->sc_width)) { 184 GPRINTF("no width property\n"); 185 goto bad; 186 } 187 if (!prop_dictionary_get_uint32(dict, "height", &sc->sc_height)) { 188 GPRINTF("no height property\n"); 189 goto bad; 190 } 191 if (!prop_dictionary_get_uint32(dict, "depth", &sc->sc_depth)) { 192 GPRINTF("no depth property\n"); 193 goto bad; 194 } 195 196 if (!prop_dictionary_get_uint64(dict, "address", &fboffset)) { 197 GPRINTF("no address property\n"); 198 goto bad; 199 } 200 201 sc->sc_fboffset = (bus_addr_t)fboffset; 202 203 sc->sc_fbaddr = NULL; 204 if (prop_dictionary_get_uint64(dict, "virtual_address", &fbaddr)) { 205 sc->sc_fbaddr = (void *)(uintptr_t)fbaddr; 206 } 207 208 scp->sc_shadowfb = NULL; 209 if (!prop_dictionary_get_bool(dict, "enable_shadowfb", 210 &scp->sc_enable_shadowfb)) 211 #ifdef GENFB_SHADOWFB 212 scp->sc_enable_shadowfb = true; 213 #else 214 scp->sc_enable_shadowfb = false; 215 #endif 216 217 if (!prop_dictionary_get_uint32(dict, "linebytes", &sc->sc_stride)) 218 sc->sc_stride = (sc->sc_width * sc->sc_depth) >> 3; 219 220 /* 221 * deal with a bug in the Raptor firmware which always sets 222 * stride = width even when depth != 8 223 */ 224 if (sc->sc_stride < sc->sc_width * (sc->sc_depth >> 3)) 225 sc->sc_stride = sc->sc_width * (sc->sc_depth >> 3); 226 227 sc->sc_fbsize = sc->sc_height * sc->sc_stride; 228 229 /* optional device colour map */ 230 scp->sc_devcmap = NULL; 231 if (prop_dictionary_get_uint64(dict, "devcmap", &devcmap)) { 232 if (devcmap != 0) 233 scp->sc_devcmap = (uint32_t *)(uintptr_t)devcmap; 234 } 235 236 /* optional colour map callback */ 237 scp->sc_cmcb = NULL; 238 if (prop_dictionary_get_uint64(dict, "cmap_callback", &cmap_cb)) { 239 if (cmap_cb != 0) 240 scp->sc_cmcb = (void *)(vaddr_t)cmap_cb; 241 } 242 243 /* optional pmf callback */ 244 sc->sc_pmfcb = NULL; 245 if (prop_dictionary_get_uint64(dict, "pmf_callback", &pmf_cb)) { 246 if (pmf_cb != 0) 247 sc->sc_pmfcb = (void *)(vaddr_t)pmf_cb; 248 } 249 250 /* optional mode callback */ 251 scp->sc_modecb = NULL; 252 if (prop_dictionary_get_uint64(dict, "mode_callback", &mode_cb)) { 253 if (mode_cb != 0) 254 scp->sc_modecb = (void *)(vaddr_t)mode_cb; 255 } 256 257 /* optional backlight control callback */ 258 scp->sc_backlight = NULL; 259 if (prop_dictionary_get_uint64(dict, "backlight_callback", &bl_cb)) { 260 if (bl_cb != 0) { 261 scp->sc_backlight = (void *)(vaddr_t)bl_cb; 262 aprint_naive_dev(sc->sc_dev, 263 "enabling backlight control\n"); 264 } 265 } 266 267 /* optional brightness control callback */ 268 scp->sc_brightness = NULL; 269 if (prop_dictionary_get_uint64(dict, "brightness_callback", &br_cb)) { 270 if (br_cb != 0) { 271 scp->sc_brightness = (void *)(vaddr_t)br_cb; 272 aprint_naive_dev(sc->sc_dev, 273 "enabling brightness control\n"); 274 if (console && 275 scp->sc_brightness->gpc_upd_parameter != NULL) { 276 pmf_event_register(sc->sc_dev, 277 PMFE_DISPLAY_BRIGHTNESS_UP, 278 genfb_brightness_up, TRUE); 279 pmf_event_register(sc->sc_dev, 280 PMFE_DISPLAY_BRIGHTNESS_DOWN, 281 genfb_brightness_down, TRUE); 282 } 283 } 284 } 285 286 return; 287 288 bad: 289 kmem_free(sc->sc_private, sizeof(*sc->sc_private)); 290 sc->sc_private = NULL; 291 } 292 293 int 294 genfb_attach(struct genfb_softc *sc, struct genfb_ops *ops) 295 { 296 struct genfb_private *scp = sc->sc_private; 297 struct wsemuldisplaydev_attach_args aa; 298 prop_dictionary_t dict; 299 struct rasops_info *ri; 300 paddr_t fb_phys; 301 uint16_t crow; 302 long defattr; 303 bool console; 304 #ifdef SPLASHSCREEN 305 int i, j; 306 int error = ENXIO; 307 #endif 308 309 KASSERTMSG(scp != NULL, "missing genfb_init"); 310 311 dict = device_properties(sc->sc_dev); 312 prop_dictionary_get_bool(dict, "is_console", &console); 313 314 if (prop_dictionary_get_uint16(dict, "cursor-row", &crow) == false) 315 crow = 0; 316 if (prop_dictionary_get_bool(dict, "clear-screen", &scp->sc_want_clear) 317 == false) 318 scp->sc_want_clear = true; 319 320 fb_phys = (paddr_t)sc->sc_fboffset; 321 if (fb_phys == 0) { 322 KASSERT(sc->sc_fbaddr != NULL); 323 (void)pmap_extract(pmap_kernel(), (vaddr_t)sc->sc_fbaddr, 324 &fb_phys); 325 } 326 327 aprint_verbose_dev(sc->sc_dev, 328 "framebuffer at %p, size %dx%d, depth %d, stride %d\n", 329 fb_phys ? (void *)(intptr_t)fb_phys : sc->sc_fbaddr, 330 sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride); 331 332 scp->sc_defaultscreen_descr = (struct wsscreen_descr){ 333 "default", 334 0, 0, 335 NULL, 336 8, 16, 337 WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE | 338 WSSCREEN_RESIZE, 339 NULL 340 }; 341 scp->sc_screens[0] = &scp->sc_defaultscreen_descr; 342 scp->sc_screenlist = (struct wsscreen_list){1, scp->sc_screens}; 343 memcpy(&scp->sc_ops, ops, sizeof(struct genfb_ops)); 344 scp->sc_mode = WSDISPLAYIO_MODE_EMUL; 345 if (scp->sc_modecb != NULL) 346 scp->sc_modecb->gmc_setmode(sc, scp->sc_mode); 347 348 scp->sc_accessops.ioctl = genfb_ioctl; 349 scp->sc_accessops.mmap = genfb_mmap; 350 scp->sc_accessops.pollc = genfb_pollc; 351 352 if (scp->sc_enable_shadowfb) { 353 scp->sc_shadowfb = kmem_alloc(sc->sc_fbsize, KM_SLEEP); 354 if (scp->sc_want_clear == false) { 355 memcpy(scp->sc_shadowfb, sc->sc_fbaddr, sc->sc_fbsize); 356 } 357 aprint_verbose_dev(sc->sc_dev, 358 "shadow framebuffer enabled, size %zu KB\n", 359 sc->sc_fbsize >> 10); 360 } 361 362 vcons_init(&sc->vd, sc, &scp->sc_defaultscreen_descr, 363 &scp->sc_accessops); 364 sc->vd.init_screen = genfb_init_screen; 365 366 /* Do not print anything between this point and the screen 367 * clear operation below. Otherwise it will be lost. */ 368 369 ri = &scp->sc_console_screen.scr_ri; 370 371 vcons_init_screen(&sc->vd, &scp->sc_console_screen, 1, 372 &defattr); 373 scp->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 374 375 #if GENFB_GLYPHCACHE > 0 376 genfb_setup_glyphcache(sc, defattr); 377 #endif 378 379 #ifdef SPLASHSCREEN 380 /* 381 * If system isn't going to go multiuser, or user has requested to see 382 * boot text, don't render splash screen immediately 383 */ 384 if (DISABLESPLASH) 385 #endif 386 vcons_redraw_screen(&scp->sc_console_screen); 387 388 scp->sc_defaultscreen_descr.textops = &ri->ri_ops; 389 scp->sc_defaultscreen_descr.capabilities = ri->ri_caps; 390 scp->sc_defaultscreen_descr.nrows = ri->ri_rows; 391 scp->sc_defaultscreen_descr.ncols = ri->ri_cols; 392 393 if (crow >= ri->ri_rows) { 394 crow = 0; 395 scp->sc_want_clear = 1; 396 } 397 398 if (console) 399 wsdisplay_cnattach(&scp->sc_defaultscreen_descr, ri, 0, crow, 400 defattr); 401 402 /* Clear the whole screen to bring it to a known state. */ 403 if (scp->sc_want_clear) 404 (*ri->ri_ops.eraserows)(ri, 0, ri->ri_rows, defattr); 405 406 #ifdef SPLASHSCREEN 407 j = 0; 408 for (i = 0; i < uimin(1 << sc->sc_depth, 256); i++) { 409 if (i >= SPLASH_CMAP_OFFSET && 410 i < SPLASH_CMAP_OFFSET + SPLASH_CMAP_SIZE) { 411 splash_get_cmap(i, 412 &scp->sc_cmap_red[i], 413 &scp->sc_cmap_green[i], 414 &scp->sc_cmap_blue[i]); 415 } else { 416 scp->sc_cmap_red[i] = rasops_cmap[j]; 417 scp->sc_cmap_green[i] = rasops_cmap[j + 1]; 418 scp->sc_cmap_blue[i] = rasops_cmap[j + 2]; 419 } 420 j += 3; 421 } 422 genfb_restore_palette(sc); 423 424 scp->sc_splash.si_depth = sc->sc_depth; 425 scp->sc_splash.si_bits = scp->sc_console_screen.scr_ri.ri_origbits; 426 scp->sc_splash.si_hwbits = sc->sc_fbaddr; 427 scp->sc_splash.si_width = sc->sc_width; 428 scp->sc_splash.si_height = sc->sc_height; 429 scp->sc_splash.si_stride = sc->sc_stride; 430 scp->sc_splash.si_fillrect = NULL; 431 if (!DISABLESPLASH) { 432 error = splash_render(&scp->sc_splash, 433 SPLASH_F_CENTER|SPLASH_F_FILL); 434 if (error) { 435 SCREEN_ENABLE_DRAWING(&scp->sc_console_screen); 436 genfb_init_palette(sc); 437 vcons_replay_msgbuf(&scp->sc_console_screen); 438 } 439 } 440 #else 441 genfb_init_palette(sc); 442 if (console && (boothowto & (AB_SILENT|AB_QUIET)) == 0) 443 vcons_replay_msgbuf(&scp->sc_console_screen); 444 #endif 445 446 if (genfb_softc == NULL) 447 genfb_softc = sc; 448 449 aa.console = console; 450 aa.scrdata = &scp->sc_screenlist; 451 aa.accessops = &scp->sc_accessops; 452 aa.accesscookie = &sc->vd; 453 454 #ifdef GENFB_DISABLE_TEXT 455 if (!DISABLESPLASH && error == 0) 456 SCREEN_DISABLE_DRAWING(&scp->sc_console_screen); 457 #endif 458 459 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint, 460 CFARGS(.iattr = "wsemuldisplaydev")); 461 462 return 0; 463 } 464 465 static int 466 genfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 467 struct lwp *l) 468 { 469 struct vcons_data *vd = v; 470 struct genfb_softc *sc = vd->cookie; 471 struct genfb_private *scp = sc->sc_private; 472 struct wsdisplay_fbinfo *wdf; 473 struct vcons_screen *ms = vd->active; 474 struct wsdisplay_param *param; 475 int new_mode, error, val, ret; 476 477 switch (cmd) { 478 case WSDISPLAYIO_GINFO: 479 if (ms == NULL) 480 return ENODEV; 481 wdf = (void *)data; 482 wdf->height = ms->scr_ri.ri_height; 483 wdf->width = ms->scr_ri.ri_width; 484 wdf->depth = ms->scr_ri.ri_depth; 485 wdf->cmsize = 256; 486 return 0; 487 488 case WSDISPLAYIO_GETCMAP: 489 return genfb_getcmap(sc, (struct wsdisplay_cmap *)data); 490 491 case WSDISPLAYIO_PUTCMAP: 492 return genfb_putcmap(sc, (struct wsdisplay_cmap *)data); 493 494 case WSDISPLAYIO_LINEBYTES: 495 *(u_int *)data = sc->sc_stride; 496 return 0; 497 498 case WSDISPLAYIO_SMODE: 499 new_mode = *(int *)data; 500 501 /* notify the bus backend */ 502 error = 0; 503 if (scp->sc_ops.genfb_ioctl) { 504 error = scp->sc_ops.genfb_ioctl(sc, vs, 505 cmd, data, flag, l); 506 } 507 if (error && error != EPASSTHROUGH) 508 return error; 509 510 if (new_mode != scp->sc_mode) { 511 scp->sc_mode = new_mode; 512 if (scp->sc_modecb != NULL) { 513 scp->sc_modecb->gmc_setmode(sc, scp->sc_mode); 514 } 515 if (new_mode == WSDISPLAYIO_MODE_EMUL) { 516 genfb_restore_palette(sc); 517 vcons_redraw_screen(ms); 518 } 519 } 520 return 0; 521 522 case WSDISPLAYIO_SSPLASH: 523 #if defined(SPLASHSCREEN) 524 if (*(int *)data == 1) { 525 SCREEN_DISABLE_DRAWING(&scp->sc_console_screen); 526 splash_render(&scp->sc_splash, 527 SPLASH_F_CENTER|SPLASH_F_FILL); 528 } else { 529 SCREEN_ENABLE_DRAWING(&scp->sc_console_screen); 530 genfb_init_palette(sc); 531 } 532 vcons_redraw_screen(ms); 533 return 0; 534 #else 535 return ENODEV; 536 #endif 537 case WSDISPLAYIO_GETPARAM: 538 param = (struct wsdisplay_param *)data; 539 switch (param->param) { 540 case WSDISPLAYIO_PARAM_BRIGHTNESS: 541 if (scp->sc_brightness == NULL) 542 return EPASSTHROUGH; 543 param->min = 0; 544 param->max = 255; 545 return scp->sc_brightness->gpc_get_parameter( 546 scp->sc_brightness->gpc_cookie, 547 ¶m->curval); 548 case WSDISPLAYIO_PARAM_BACKLIGHT: 549 if (scp->sc_backlight == NULL) 550 return EPASSTHROUGH; 551 param->min = 0; 552 param->max = 1; 553 return scp->sc_backlight->gpc_get_parameter( 554 scp->sc_backlight->gpc_cookie, 555 ¶m->curval); 556 } 557 return EPASSTHROUGH; 558 559 case WSDISPLAYIO_SETPARAM: 560 param = (struct wsdisplay_param *)data; 561 switch (param->param) { 562 case WSDISPLAYIO_PARAM_BRIGHTNESS: 563 if (scp->sc_brightness == NULL) 564 return EPASSTHROUGH; 565 val = param->curval; 566 if (val < 0) 567 val = 0; 568 if (val > 255) 569 val = 255; 570 return scp->sc_brightness->gpc_set_parameter( 571 scp->sc_brightness->gpc_cookie, val); 572 case WSDISPLAYIO_PARAM_BACKLIGHT: 573 if (scp->sc_backlight == NULL) 574 return EPASSTHROUGH; 575 val = param->curval; 576 if (val < 0) 577 val = 0; 578 if (val > 1) 579 val = 1; 580 return scp->sc_backlight->gpc_set_parameter( 581 scp->sc_backlight->gpc_cookie, val); 582 } 583 return EPASSTHROUGH; 584 } 585 ret = EPASSTHROUGH; 586 if (scp->sc_ops.genfb_ioctl) 587 ret = scp->sc_ops.genfb_ioctl(sc, vs, cmd, data, flag, l); 588 if (ret != EPASSTHROUGH) 589 return ret; 590 /* 591 * XXX 592 * handle these only if there either is no ioctl() handler or it didn't 593 * know how to deal with them. This allows bus frontends to override 594 * them but still provides fallback implementations 595 */ 596 switch (cmd) { 597 case WSDISPLAYIO_GET_EDID: 598 { 599 struct wsdisplayio_edid_info *d = data; 600 return wsdisplayio_get_edid(sc->sc_dev, d); 601 } 602 case WSDISPLAYIO_GET_FBINFO: 603 { 604 struct wsdisplayio_fbinfo *fbi = data; 605 return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); 606 } 607 } 608 return EPASSTHROUGH; 609 } 610 611 static paddr_t 612 genfb_mmap(void *v, void *vs, off_t offset, int prot) 613 { 614 struct vcons_data *vd = v; 615 struct genfb_softc *sc = vd->cookie; 616 struct genfb_private *scp = sc->sc_private; 617 618 if (scp->sc_ops.genfb_mmap) 619 return scp->sc_ops.genfb_mmap(sc, vs, offset, prot); 620 621 return -1; 622 } 623 624 static void 625 genfb_pollc(void *v, int on) 626 { 627 struct vcons_data *vd = v; 628 struct genfb_softc *sc = vd->cookie; 629 630 if (sc == NULL) 631 return; 632 633 if (on) 634 genfb_enable_polling(sc->sc_dev); 635 else 636 genfb_disable_polling(sc->sc_dev); 637 } 638 639 static void 640 genfb_init_screen(void *cookie, struct vcons_screen *scr, 641 int existing, long *defattr) 642 { 643 struct genfb_softc *sc = cookie; 644 struct genfb_private *scp = sc->sc_private; 645 struct rasops_info *ri = &scr->scr_ri; 646 int wantcols; 647 bool is_bgr, is_swapped, is_10bit; 648 649 ri->ri_depth = sc->sc_depth; 650 ri->ri_width = sc->sc_width; 651 ri->ri_height = sc->sc_height; 652 ri->ri_stride = sc->sc_stride; 653 ri->ri_flg = RI_CENTER; 654 if (scp->sc_want_clear) 655 ri->ri_flg |= RI_FULLCLEAR; 656 657 scr->scr_flags |= VCONS_LOADFONT; 658 659 if (scp->sc_shadowfb != NULL) { 660 ri->ri_hwbits = (char *)sc->sc_fbaddr; 661 ri->ri_bits = (char *)scp->sc_shadowfb; 662 } else { 663 ri->ri_bits = (char *)sc->sc_fbaddr; 664 scr->scr_flags |= VCONS_DONT_READ; 665 } 666 667 if (existing && scp->sc_want_clear) 668 ri->ri_flg |= RI_CLEAR; 669 670 switch (ri->ri_depth) { 671 case 32: 672 case 24: 673 ri->ri_flg |= RI_ENABLE_ALPHA; 674 675 is_bgr = false; 676 prop_dictionary_get_bool(device_properties(sc->sc_dev), 677 "is_bgr", &is_bgr); 678 679 is_swapped = false; 680 prop_dictionary_get_bool(device_properties(sc->sc_dev), 681 "is_swapped", &is_swapped); 682 683 is_10bit = false; 684 prop_dictionary_get_bool(device_properties(sc->sc_dev), 685 "is_10bit", &is_10bit); 686 687 const int bits = is_10bit ? 10 : 8; 688 ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = bits; 689 690 if (is_bgr) { 691 /* someone requested BGR */ 692 ri->ri_rpos = bits * 0; 693 ri->ri_gpos = bits * 1; 694 ri->ri_bpos = bits * 2; 695 } else if (is_swapped) { 696 /* byte-swapped, must be 32 bpp */ 697 KASSERT(ri->ri_depth == 32); 698 KASSERT(bits == 8); 699 ri->ri_rpos = 8; 700 ri->ri_gpos = 16; 701 ri->ri_bpos = 24; 702 } else { 703 /* assume RGB */ 704 ri->ri_rpos = bits * 2; 705 ri->ri_gpos = bits * 1; 706 ri->ri_bpos = bits * 0; 707 } 708 break; 709 710 case 16: 711 case 15: 712 ri->ri_flg |= RI_ENABLE_ALPHA; 713 break; 714 715 case 8: 716 if (scp->sc_cmcb != NULL) 717 ri->ri_flg |= RI_ENABLE_ALPHA | RI_8BIT_IS_RGB; 718 break; 719 720 case 2: 721 ri->ri_flg |= RI_ENABLE_ALPHA; 722 break; 723 724 default: 725 break; 726 } 727 728 wantcols = genfb_calc_cols(sc, ri); 729 730 rasops_init(ri, 0, wantcols); 731 ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE | 732 WSSCREEN_RESIZE; 733 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 734 sc->sc_width / ri->ri_font->fontwidth); 735 736 if (scp->sc_devcmap != NULL) { 737 memcpy(ri->ri_devcmap, scp->sc_devcmap, sizeof(ri->ri_devcmap)); 738 } 739 740 ri->ri_hw = scr; 741 #if GENFB_GLYPHCACHE > 0 742 scp->sc_putchar = ri->ri_ops.putchar; 743 ri->ri_ops.putchar = genfb_putchar; 744 #endif 745 #ifdef GENFB_DISABLE_TEXT 746 if (scr == &scp->sc_console_screen && !DISABLESPLASH) 747 SCREEN_DISABLE_DRAWING(&scp->sc_console_screen); 748 #endif 749 } 750 751 /* Returns the width of the display in millimeters, or 0 if not known. */ 752 static int 753 genfb_calc_hsize(struct genfb_softc *sc) 754 { 755 device_t dev = sc->sc_dev; 756 prop_dictionary_t dict = device_properties(dev); 757 prop_data_t edid_data; 758 struct edid_info *edid; 759 const char *edid_ptr; 760 int hsize; 761 762 edid_data = prop_dictionary_get(dict, "EDID"); 763 if (edid_data == NULL || prop_data_size(edid_data) < 128) 764 return 0; 765 766 edid = kmem_alloc(sizeof(*edid), KM_SLEEP); 767 768 edid_ptr = prop_data_value(edid_data); 769 if (edid_parse(__UNCONST(edid_ptr), edid) == 0) 770 hsize = (int)edid->edid_max_hsize * 10; 771 else 772 hsize = 0; 773 774 kmem_free(edid, sizeof(*edid)); 775 776 return hsize; 777 } 778 779 /* Return the minimum number of character columns based on DPI */ 780 static int 781 genfb_calc_cols(struct genfb_softc *sc, struct rasops_info *ri) 782 { 783 const int hsize = genfb_calc_hsize(sc); 784 785 if (hsize != 0) { 786 ri->ri_flg |= RI_PREFER_WIDEFONT; 787 } 788 789 return MAX(RASOPS_DEFAULT_WIDTH, hsize / GENFB_CHAR_WIDTH_MM); 790 } 791 792 static int 793 genfb_putcmap(struct genfb_softc *sc, struct wsdisplay_cmap *cm) 794 { 795 struct genfb_private *scp = sc->sc_private; 796 u_char *r, *g, *b; 797 u_int index = cm->index; 798 u_int count = cm->count; 799 int i, error; 800 u_char rbuf[256], gbuf[256], bbuf[256]; 801 802 #ifdef GENFB_DEBUG 803 aprint_debug("putcmap: %d %d\n",index, count); 804 #endif 805 if (index >= 256 || count > 256 || index + count > 256) 806 return EINVAL; 807 808 error = copyin(cm->red, &rbuf[index], count); 809 if (error) 810 return error; 811 error = copyin(cm->green, &gbuf[index], count); 812 if (error) 813 return error; 814 error = copyin(cm->blue, &bbuf[index], count); 815 if (error) 816 return error; 817 818 memcpy(&scp->sc_cmap_red[index], &rbuf[index], count); 819 memcpy(&scp->sc_cmap_green[index], &gbuf[index], count); 820 memcpy(&scp->sc_cmap_blue[index], &bbuf[index], count); 821 822 r = &scp->sc_cmap_red[index]; 823 g = &scp->sc_cmap_green[index]; 824 b = &scp->sc_cmap_blue[index]; 825 826 for (i = 0; i < count; i++) { 827 genfb_putpalreg(sc, index, *r, *g, *b); 828 index++; 829 r++, g++, b++; 830 } 831 return 0; 832 } 833 834 static int 835 genfb_getcmap(struct genfb_softc *sc, struct wsdisplay_cmap *cm) 836 { 837 struct genfb_private *scp = sc->sc_private; 838 u_int index = cm->index; 839 u_int count = cm->count; 840 int error; 841 842 if (index >= 256 || count > 256 || index + count > 256) 843 return EINVAL; 844 845 error = copyout(&scp->sc_cmap_red[index], cm->red, count); 846 if (error) 847 return error; 848 error = copyout(&scp->sc_cmap_green[index], cm->green, count); 849 if (error) 850 return error; 851 error = copyout(&scp->sc_cmap_blue[index], cm->blue, count); 852 if (error) 853 return error; 854 855 return 0; 856 } 857 858 void 859 genfb_restore_palette(struct genfb_softc *sc) 860 { 861 struct genfb_private *scp = sc->sc_private; 862 int i; 863 864 if (sc->sc_depth <= 8) { 865 for (i = 0; i < (1 << sc->sc_depth); i++) { 866 genfb_putpalreg(sc, i, scp->sc_cmap_red[i], 867 scp->sc_cmap_green[i], scp->sc_cmap_blue[i]); 868 } 869 } 870 } 871 872 static void 873 genfb_init_palette(struct genfb_softc *sc) 874 { 875 struct genfb_private *scp = sc->sc_private; 876 int i, j, tmp; 877 878 if (sc->sc_depth == 8) { 879 /* generate an r3g3b2 colour map */ 880 for (i = 0; i < 256; i++) { 881 tmp = i & 0xe0; 882 /* 883 * replicate bits so 0xe0 maps to a red value of 0xff 884 * in order to make white look actually white 885 */ 886 tmp |= (tmp >> 3) | (tmp >> 6); 887 scp->sc_cmap_red[i] = tmp; 888 889 tmp = (i & 0x1c) << 3; 890 tmp |= (tmp >> 3) | (tmp >> 6); 891 scp->sc_cmap_green[i] = tmp; 892 893 tmp = (i & 0x03) << 6; 894 tmp |= tmp >> 2; 895 tmp |= tmp >> 4; 896 scp->sc_cmap_blue[i] = tmp; 897 898 genfb_putpalreg(sc, i, scp->sc_cmap_red[i], 899 scp->sc_cmap_green[i], 900 scp->sc_cmap_blue[i]); 901 } 902 } else { 903 /* steal rasops' ANSI cmap */ 904 j = 0; 905 for (i = 0; i < 256; i++) { 906 scp->sc_cmap_red[i] = rasops_cmap[j]; 907 scp->sc_cmap_green[i] = rasops_cmap[j + 1]; 908 scp->sc_cmap_blue[i] = rasops_cmap[j + 2]; 909 j += 3; 910 } 911 } 912 } 913 914 static int 915 genfb_putpalreg(struct genfb_softc *sc, uint8_t idx, uint8_t r, uint8_t g, 916 uint8_t b) 917 { 918 struct genfb_private *scp = sc->sc_private; 919 920 if (scp->sc_cmcb) { 921 scp->sc_cmcb->gcc_set_mapreg(scp->sc_cmcb->gcc_cookie, 922 idx, r, g, b); 923 } 924 return 0; 925 } 926 927 void 928 genfb_cnattach(void) 929 { 930 931 genfb_cnattach_called = 1; 932 } 933 934 int 935 genfb_cndetach(void) 936 { 937 938 if (genfb_cnattach_called) { 939 genfb_cnattach_called = 0; 940 return 1; 941 } 942 return 0; 943 } 944 945 void 946 genfb_disable(void) 947 { 948 949 genfb_enabled = 0; 950 } 951 952 int 953 genfb_is_console(void) 954 { 955 956 return genfb_cnattach_called; 957 } 958 959 int 960 genfb_is_enabled(void) 961 { 962 963 return genfb_enabled; 964 } 965 966 int 967 genfb_borrow(bus_addr_t addr, bus_space_handle_t *hdlp) 968 { 969 struct genfb_softc *sc = genfb_softc; 970 971 if (sc && sc->sc_private && sc->sc_private->sc_ops.genfb_borrow) 972 return sc->sc_private->sc_ops.genfb_borrow(sc, addr, hdlp); 973 return 0; 974 } 975 976 static void 977 genfb_brightness_up(device_t dev) 978 { 979 struct genfb_softc *sc = device_private(dev); 980 struct genfb_private *scp = sc->sc_private; 981 982 KASSERT(scp->sc_brightness != NULL); 983 KASSERT(scp->sc_brightness->gpc_upd_parameter != NULL); 984 985 (void)scp->sc_brightness->gpc_upd_parameter( 986 scp->sc_brightness->gpc_cookie, GENFB_BRIGHTNESS_STEP); 987 } 988 989 static void 990 genfb_brightness_down(device_t dev) 991 { 992 struct genfb_softc *sc = device_private(dev); 993 struct genfb_private *scp = sc->sc_private; 994 995 KASSERT(scp->sc_brightness != NULL); 996 KASSERT(scp->sc_brightness->gpc_upd_parameter != NULL); 997 998 (void)scp->sc_brightness->gpc_upd_parameter( 999 scp->sc_brightness->gpc_cookie, -GENFB_BRIGHTNESS_STEP); 1000 } 1001 1002 void 1003 genfb_enable_polling(device_t dev) 1004 { 1005 struct genfb_softc *sc = device_private(dev); 1006 struct genfb_private *scp = sc->sc_private; 1007 1008 if (scp == NULL) 1009 return; 1010 1011 if (scp->sc_console_screen.scr_vd) { 1012 SCREEN_ENABLE_DRAWING(&scp->sc_console_screen); 1013 vcons_hard_switch(&scp->sc_console_screen); 1014 vcons_enable_polling(&sc->vd); 1015 if (scp->sc_ops.genfb_enable_polling) 1016 (*scp->sc_ops.genfb_enable_polling)(sc); 1017 } 1018 } 1019 1020 void 1021 genfb_disable_polling(device_t dev) 1022 { 1023 struct genfb_softc *sc = device_private(dev); 1024 struct genfb_private *scp = sc->sc_private; 1025 1026 if (scp == NULL) 1027 return; 1028 1029 if (scp->sc_console_screen.scr_vd) { 1030 if (scp->sc_ops.genfb_disable_polling) 1031 (*scp->sc_ops.genfb_disable_polling)(sc); 1032 vcons_disable_polling(&sc->vd); 1033 } 1034 } 1035 1036 #if GENFB_GLYPHCACHE > 0 1037 #define GLYPHCACHESIZE ((GENFB_GLYPHCACHE) * 1024 * 1024) 1038 1039 static inline int 1040 attr2idx(long attr) 1041 { 1042 if ((attr & 0xf0f00ff8) != 0) 1043 return -1; 1044 1045 return (((attr >> 16) & 0x0f) | ((attr >> 20) & 0xf0)); 1046 } 1047 1048 static int 1049 genfb_setup_glyphcache(struct genfb_softc *sc, long defattr) 1050 { 1051 struct genfb_private *scp = sc->sc_private; 1052 struct rasops_info *ri = &scp->sc_console_screen.scr_ri, 1053 *cri = &scp->sc_cache_ri; 1054 gc_bucket *b; 1055 int i, usedcells = 0, idx, j; 1056 1057 scp->sc_cache = kmem_alloc(GLYPHCACHESIZE, KM_SLEEP); 1058 1059 /* 1060 * now we build a mutant rasops_info for the cache - same pixel type 1061 * and such as the real fb, but only one character per line for 1062 * simplicity and locality 1063 */ 1064 memcpy(cri, ri, sizeof(struct rasops_info)); 1065 cri->ri_ops.putchar = scp->sc_putchar; 1066 cri->ri_width = ri->ri_font->fontwidth; 1067 cri->ri_stride = ri->ri_xscale; 1068 cri->ri_bits = scp->sc_cache; 1069 cri->ri_hwbits = NULL; 1070 cri->ri_origbits = scp->sc_cache; 1071 cri->ri_cols = 1; 1072 cri->ri_rows = GLYPHCACHESIZE / 1073 (cri->ri_stride * cri->ri_font->fontheight); 1074 cri->ri_xorigin = 0; 1075 cri->ri_yorigin = 0; 1076 cri->ri_xscale = ri->ri_xscale; 1077 cri->ri_yscale = ri->ri_font->fontheight * ri->ri_xscale; 1078 1079 printf("size %d %d %d\n", GLYPHCACHESIZE, ri->ri_width, ri->ri_stride); 1080 printf("cells: %d\n", cri->ri_rows); 1081 scp->sc_nbuckets = uimin(256, cri->ri_rows / 223); 1082 scp->sc_buckets = kmem_alloc(sizeof(gc_bucket) * scp->sc_nbuckets, 1083 KM_SLEEP); 1084 printf("buckets: %d\n", scp->sc_nbuckets); 1085 for (i = 0; i < scp->sc_nbuckets; i++) { 1086 b = &scp->sc_buckets[i]; 1087 b->gb_firstcell = usedcells; 1088 b->gb_numcells = uimin(223, cri->ri_rows - usedcells); 1089 usedcells += 223; 1090 b->gb_usedcells = 0; 1091 b->gb_index = -1; 1092 for (j = 0; j < 223; j++) b->gb_map[j] = -1; 1093 } 1094 1095 /* initialize the attribute map... */ 1096 for (i = 0; i < 256; i++) { 1097 scp->sc_attrmap[i] = -1; 1098 } 1099 1100 /* first bucket goes to default attr */ 1101 idx = attr2idx(defattr); 1102 printf("defattr %08lx idx %x\n", defattr, idx); 1103 1104 if (idx >= 0) { 1105 scp->sc_attrmap[idx] = 0; 1106 scp->sc_buckets[0].gb_index = idx; 1107 } 1108 1109 return 0; 1110 } 1111 1112 static void 1113 genfb_putchar(void *cookie, int row, int col, u_int c, long attr) 1114 { 1115 struct rasops_info *ri = cookie; 1116 struct vcons_screen *scr = ri->ri_hw; 1117 struct genfb_softc *sc = scr->scr_cookie; 1118 struct genfb_private *scp = sc->sc_private; 1119 uint8_t *src, *dst, *hwdst; 1120 gc_bucket *b; 1121 int i, idx, bi, cell; 1122 1123 attr &= ~WSATTR_USERMASK; 1124 1125 idx = attr2idx(attr); 1126 if (c < 33 || c > 255 || idx < 0) goto nope; 1127 1128 /* look for a bucket with the right attribute */ 1129 bi = scp->sc_attrmap[idx]; 1130 if (bi == -1) { 1131 /* nope, see if there's an empty one left */ 1132 bi = 1; 1133 while ((bi < scp->sc_nbuckets) && 1134 (scp->sc_buckets[bi].gb_index != -1)) { 1135 bi++; 1136 } 1137 if (bi < scp->sc_nbuckets) { 1138 /* found one -> grab it */ 1139 scp->sc_attrmap[idx] = bi; 1140 b = &scp->sc_buckets[bi]; 1141 b->gb_index = idx; 1142 b->gb_usedcells = 0; 1143 /* make sure this doesn't get evicted right away */ 1144 b->gb_lastread = time_uptime; 1145 } else { 1146 /* 1147 * still nothing 1148 * steal the least recently read bucket 1149 */ 1150 time_t moo = time_uptime; 1151 int oldest = 1; 1152 1153 for (i = 1; i < scp->sc_nbuckets; i++) { 1154 if (scp->sc_buckets[i].gb_lastread < moo) { 1155 oldest = i; 1156 moo = scp->sc_buckets[i].gb_lastread; 1157 } 1158 } 1159 1160 /* if we end up here all buckets must be in use */ 1161 b = &scp->sc_buckets[oldest]; 1162 scp->sc_attrmap[b->gb_index] = -1; 1163 b->gb_index = idx; 1164 b->gb_usedcells = 0; 1165 scp->sc_attrmap[idx] = oldest; 1166 /* now scrub it */ 1167 for (i = 0; i < 223; i++) 1168 b->gb_map[i] = -1; 1169 /* and set the time stamp */ 1170 b->gb_lastread = time_uptime; 1171 } 1172 } else { 1173 /* found one */ 1174 b = &scp->sc_buckets[bi]; 1175 } 1176 1177 /* see if there's room in the bucket */ 1178 if (b->gb_usedcells >= b->gb_numcells) goto nope; 1179 1180 cell = b->gb_map[c - 33]; 1181 if (cell == -1) { 1182 if (b->gb_usedcells >= b->gb_numcells) 1183 goto nope; 1184 cell = atomic_add_int_nv(&b->gb_usedcells, 1) - 1; 1185 b->gb_map[c - 33] = cell; 1186 cell += b->gb_firstcell; 1187 scp->sc_putchar(&scp->sc_cache_ri, cell, 0, c, attr); 1188 } else 1189 cell += b->gb_firstcell; 1190 1191 src = scp->sc_cache + cell * scp->sc_cache_ri.ri_yscale; 1192 dst = ri->ri_bits + row * ri->ri_yscale + col * ri->ri_xscale; 1193 for (i = 0; i < ri->ri_font->fontheight; i++) { 1194 memcpy(dst, src, ri->ri_xscale); 1195 src += ri->ri_xscale; 1196 dst += ri->ri_stride; 1197 } 1198 if (ri->ri_hwbits) { 1199 src = scp->sc_cache + cell * scp->sc_cache_ri.ri_yscale; 1200 hwdst = ri->ri_hwbits 1201 + row * ri->ri_yscale + col * ri->ri_xscale; 1202 for (i = 0; i < ri->ri_font->fontheight; i++) { 1203 memcpy(hwdst, src, ri->ri_xscale); 1204 src += ri->ri_xscale; 1205 hwdst += ri->ri_stride; 1206 } 1207 } 1208 b->gb_lastread = time_uptime; 1209 return; 1210 nope: 1211 scp->sc_putchar(cookie, row, col, c, attr); 1212 } 1213 1214 #endif 1215