1/* $NetBSD: genfb.c,v 1.91 2024/01/20 00:24:58 jmcneill 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.91 2024/01/20 00:24:58 jmcneill 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 78struct 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 137static int genfb_ioctl(void *, void *, u_long, void *, int, struct lwp *); 138static paddr_t genfb_mmap(void *, void *, off_t, int); 139static void genfb_pollc(void *, int); 140 141static void genfb_init_screen(void *, struct vcons_screen *, int, long *); 142static int genfb_calc_hsize(struct genfb_softc *); 143static int genfb_calc_cols(struct genfb_softc *, struct rasops_info *); 144 145static int genfb_putcmap(struct genfb_softc *, struct wsdisplay_cmap *); 146static int genfb_getcmap(struct genfb_softc *, struct wsdisplay_cmap *); 147static int genfb_putpalreg(struct genfb_softc *, uint8_t, uint8_t, 148 uint8_t, uint8_t); 149static void genfb_init_palette(struct genfb_softc *); 150 151static void genfb_brightness_up(device_t); 152static void genfb_brightness_down(device_t); 153 154#if GENFB_GLYPHCACHE > 0 155static int genfb_setup_glyphcache(struct genfb_softc *, long); 156static void genfb_putchar(void *, int, int, u_int, long); 157#endif 158 159extern const u_char rasops_cmap[768]; 160 161static int genfb_cnattach_called = 0; 162static int genfb_enabled = 1; 163 164static struct genfb_softc *genfb_softc = NULL; 165 166void 167genfb_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 288bad: kmem_free(sc->sc_private, sizeof(*sc->sc_private)); 289 sc->sc_private = NULL; 290} 291 292int 293genfb_attach(struct genfb_softc *sc, struct genfb_ops *ops) 294{ 295 struct genfb_private *scp = sc->sc_private; 296 struct wsemuldisplaydev_attach_args aa; 297 prop_dictionary_t dict; 298 struct rasops_info *ri; 299 paddr_t fb_phys; 300 uint16_t crow; 301 long defattr; 302 bool console; 303#ifdef SPLASHSCREEN 304 int i, j; 305 int error = ENXIO; 306#endif 307 308 KASSERTMSG(scp != NULL, "missing genfb_init"); 309 310 dict = device_properties(sc->sc_dev); 311 prop_dictionary_get_bool(dict, "is_console", &console); 312 313 if (prop_dictionary_get_uint16(dict, "cursor-row", &crow) == false) 314 crow = 0; 315 if (prop_dictionary_get_bool(dict, "clear-screen", &scp->sc_want_clear) 316 == false) 317 scp->sc_want_clear = true; 318 319 fb_phys = (paddr_t)sc->sc_fboffset; 320 if (fb_phys == 0) { 321 KASSERT(sc->sc_fbaddr != NULL); 322 (void)pmap_extract(pmap_kernel(), (vaddr_t)sc->sc_fbaddr, 323 &fb_phys); 324 } 325 326 aprint_verbose_dev(sc->sc_dev, "framebuffer at %p, size %dx%d, depth %d, " 327 "stride %d\n", 328 fb_phys ? (void *)(intptr_t)fb_phys : sc->sc_fbaddr, 329 sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride); 330 331 scp->sc_defaultscreen_descr = (struct wsscreen_descr){ 332 "default", 333 0, 0, 334 NULL, 335 8, 16, 336 WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE | 337 WSSCREEN_RESIZE, 338 NULL 339 }; 340 scp->sc_screens[0] = &scp->sc_defaultscreen_descr; 341 scp->sc_screenlist = (struct wsscreen_list){1, scp->sc_screens}; 342 memcpy(&scp->sc_ops, ops, sizeof(struct genfb_ops)); 343 scp->sc_mode = WSDISPLAYIO_MODE_EMUL; 344 if (scp->sc_modecb != NULL) 345 scp->sc_modecb->gmc_setmode(sc, scp->sc_mode); 346 347 scp->sc_accessops.ioctl = genfb_ioctl; 348 scp->sc_accessops.mmap = genfb_mmap; 349 scp->sc_accessops.pollc = genfb_pollc; 350 351 if (scp->sc_enable_shadowfb) { 352 scp->sc_shadowfb = kmem_alloc(sc->sc_fbsize, KM_SLEEP); 353 if (scp->sc_want_clear == false) { 354 memcpy(scp->sc_shadowfb, sc->sc_fbaddr, sc->sc_fbsize); 355 } 356 aprint_verbose_dev(sc->sc_dev, 357 "shadow framebuffer enabled, size %zu KB\n", 358 sc->sc_fbsize >> 10); 359 } 360 361 vcons_init(&sc->vd, sc, &scp->sc_defaultscreen_descr, 362 &scp->sc_accessops); 363 sc->vd.init_screen = genfb_init_screen; 364 365 /* Do not print anything between this point and the screen 366 * clear operation below. Otherwise it will be lost. */ 367 368 ri = &scp->sc_console_screen.scr_ri; 369 370 vcons_init_screen(&sc->vd, &scp->sc_console_screen, 1, 371 &defattr); 372 scp->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 373 374#if GENFB_GLYPHCACHE > 0 375 genfb_setup_glyphcache(sc, defattr); 376#endif 377 378#ifdef SPLASHSCREEN 379/* 380 * If system isn't going to go multiuser, or user has requested to see 381 * boot text, don't render splash screen immediately 382 */ 383 if (DISABLESPLASH) 384#endif 385 vcons_redraw_screen(&scp->sc_console_screen); 386 387 scp->sc_defaultscreen_descr.textops = &ri->ri_ops; 388 scp->sc_defaultscreen_descr.capabilities = ri->ri_caps; 389 scp->sc_defaultscreen_descr.nrows = ri->ri_rows; 390 scp->sc_defaultscreen_descr.ncols = ri->ri_cols; 391 392 if (crow >= ri->ri_rows) { 393 crow = 0; 394 scp->sc_want_clear = 1; 395 } 396 397 if (console) 398 wsdisplay_cnattach(&scp->sc_defaultscreen_descr, ri, 0, crow, 399 defattr); 400 401 /* Clear the whole screen to bring it to a known state. */ 402 if (scp->sc_want_clear) 403 (*ri->ri_ops.eraserows)(ri, 0, ri->ri_rows, defattr); 404 405#ifdef SPLASHSCREEN 406 j = 0; 407 for (i = 0; i < uimin(1 << sc->sc_depth, 256); i++) { 408 if (i >= SPLASH_CMAP_OFFSET && 409 i < SPLASH_CMAP_OFFSET + SPLASH_CMAP_SIZE) { 410 splash_get_cmap(i, 411 &scp->sc_cmap_red[i], 412 &scp->sc_cmap_green[i], 413 &scp->sc_cmap_blue[i]); 414 } else { 415 scp->sc_cmap_red[i] = rasops_cmap[j]; 416 scp->sc_cmap_green[i] = rasops_cmap[j + 1]; 417 scp->sc_cmap_blue[i] = rasops_cmap[j + 2]; 418 } 419 j += 3; 420 } 421 genfb_restore_palette(sc); 422 423 scp->sc_splash.si_depth = sc->sc_depth; 424 scp->sc_splash.si_bits = scp->sc_console_screen.scr_ri.ri_origbits; 425 scp->sc_splash.si_hwbits = sc->sc_fbaddr; 426 scp->sc_splash.si_width = sc->sc_width; 427 scp->sc_splash.si_height = sc->sc_height; 428 scp->sc_splash.si_stride = sc->sc_stride; 429 scp->sc_splash.si_fillrect = NULL; 430 if (!DISABLESPLASH) { 431 error = splash_render(&scp->sc_splash, 432 SPLASH_F_CENTER|SPLASH_F_FILL); 433 if (error) { 434 SCREEN_ENABLE_DRAWING(&scp->sc_console_screen); 435 genfb_init_palette(sc); 436 vcons_replay_msgbuf(&scp->sc_console_screen); 437 } 438 } 439#else 440 genfb_init_palette(sc); 441 if (console && (boothowto & (AB_SILENT|AB_QUIET)) == 0) 442 vcons_replay_msgbuf(&scp->sc_console_screen); 443#endif 444 445 if (genfb_softc == NULL) 446 genfb_softc = sc; 447 448 aa.console = console; 449 aa.scrdata = &scp->sc_screenlist; 450 aa.accessops = &scp->sc_accessops; 451 aa.accesscookie = &sc->vd; 452 453#ifdef GENFB_DISABLE_TEXT 454 if (!DISABLESPLASH && error == 0) 455 SCREEN_DISABLE_DRAWING(&scp->sc_console_screen); 456#endif 457 458 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint, 459 CFARGS(.iattr = "wsemuldisplaydev")); 460 461 return 0; 462} 463 464static int 465genfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 466 struct lwp *l) 467{ 468 struct vcons_data *vd = v; 469 struct genfb_softc *sc = vd->cookie; 470 struct genfb_private *scp = sc->sc_private; 471 struct wsdisplay_fbinfo *wdf; 472 struct vcons_screen *ms = vd->active; 473 struct wsdisplay_param *param; 474 int new_mode, error, val, ret; 475 476 switch (cmd) { 477 case WSDISPLAYIO_GINFO: 478 if (ms == NULL) 479 return ENODEV; 480 wdf = (void *)data; 481 wdf->height = ms->scr_ri.ri_height; 482 wdf->width = ms->scr_ri.ri_width; 483 wdf->depth = ms->scr_ri.ri_depth; 484 wdf->cmsize = 256; 485 return 0; 486 487 case WSDISPLAYIO_GETCMAP: 488 return genfb_getcmap(sc, 489 (struct wsdisplay_cmap *)data); 490 491 case WSDISPLAYIO_PUTCMAP: 492 return genfb_putcmap(sc, 493 (struct wsdisplay_cmap *)data); 494 495 case WSDISPLAYIO_LINEBYTES: 496 *(u_int *)data = sc->sc_stride; 497 return 0; 498 499 case WSDISPLAYIO_SMODE: 500 new_mode = *(int *)data; 501 502 /* notify the bus backend */ 503 error = 0; 504 if (scp->sc_ops.genfb_ioctl) { 505 error = scp->sc_ops.genfb_ioctl(sc, vs, 506 cmd, data, flag, l); 507 } 508 if (error && error != EPASSTHROUGH) 509 return error; 510 511 if (new_mode != scp->sc_mode) { 512 scp->sc_mode = new_mode; 513 if (scp->sc_modecb != NULL) { 514 scp->sc_modecb->gmc_setmode(sc, 515 scp->sc_mode); 516 } 517 if (new_mode == WSDISPLAYIO_MODE_EMUL) { 518 genfb_restore_palette(sc); 519 vcons_redraw_screen(ms); 520 } 521 } 522 return 0; 523 524 case WSDISPLAYIO_SSPLASH: 525#if defined(SPLASHSCREEN) 526 if(*(int *)data == 1) { 527 SCREEN_DISABLE_DRAWING(&scp->sc_console_screen); 528 splash_render(&scp->sc_splash, 529 SPLASH_F_CENTER|SPLASH_F_FILL); 530 } else { 531 SCREEN_ENABLE_DRAWING(&scp->sc_console_screen); 532 genfb_init_palette(sc); 533 } 534 vcons_redraw_screen(ms); 535 return 0; 536#else 537 return ENODEV; 538#endif 539 case WSDISPLAYIO_GETPARAM: 540 param = (struct wsdisplay_param *)data; 541 switch (param->param) { 542 case WSDISPLAYIO_PARAM_BRIGHTNESS: 543 if (scp->sc_brightness == NULL) 544 return EPASSTHROUGH; 545 param->min = 0; 546 param->max = 255; 547 return scp->sc_brightness->gpc_get_parameter( 548 scp->sc_brightness->gpc_cookie, 549 ¶m->curval); 550 case WSDISPLAYIO_PARAM_BACKLIGHT: 551 if (scp->sc_backlight == NULL) 552 return EPASSTHROUGH; 553 param->min = 0; 554 param->max = 1; 555 return scp->sc_backlight->gpc_get_parameter( 556 scp->sc_backlight->gpc_cookie, 557 ¶m->curval); 558 } 559 return EPASSTHROUGH; 560 561 case WSDISPLAYIO_SETPARAM: 562 param = (struct wsdisplay_param *)data; 563 switch (param->param) { 564 case WSDISPLAYIO_PARAM_BRIGHTNESS: 565 if (scp->sc_brightness == NULL) 566 return EPASSTHROUGH; 567 val = param->curval; 568 if (val < 0) val = 0; 569 if (val > 255) 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) val = 0; 577 if (val > 1) val = 1; 578 return scp->sc_backlight->gpc_set_parameter( 579 scp->sc_backlight->gpc_cookie, val); 580 } 581 return EPASSTHROUGH; 582 } 583 ret = EPASSTHROUGH; 584 if (scp->sc_ops.genfb_ioctl) 585 ret = scp->sc_ops.genfb_ioctl(sc, vs, cmd, data, flag, l); 586 if (ret != EPASSTHROUGH) 587 return ret; 588 /* 589 * XXX 590 * handle these only if there either is no ioctl() handler or it didn't 591 * know how to deal with them. This allows bus frontends to override 592 * them but still provides fallback implementations 593 */ 594 switch (cmd) { 595 case WSDISPLAYIO_GET_EDID: { 596 struct wsdisplayio_edid_info *d = data; 597 return wsdisplayio_get_edid(sc->sc_dev, d); 598 } 599 case WSDISPLAYIO_GET_FBINFO: { 600 struct wsdisplayio_fbinfo *fbi = data; 601 return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); 602 } 603 } 604 return EPASSTHROUGH; 605} 606 607static paddr_t 608genfb_mmap(void *v, void *vs, off_t offset, int prot) 609{ 610 struct vcons_data *vd = v; 611 struct genfb_softc *sc = vd->cookie; 612 struct genfb_private *scp = sc->sc_private; 613 614 if (scp->sc_ops.genfb_mmap) 615 return scp->sc_ops.genfb_mmap(sc, vs, offset, prot); 616 617 return -1; 618} 619 620static void 621genfb_pollc(void *v, int on) 622{ 623 struct vcons_data *vd = v; 624 struct genfb_softc *sc = vd->cookie; 625 626 if (sc == NULL) 627 return; 628 629 if (on) 630 genfb_enable_polling(sc->sc_dev); 631 else 632 genfb_disable_polling(sc->sc_dev); 633} 634 635static void 636genfb_init_screen(void *cookie, struct vcons_screen *scr, 637 int existing, long *defattr) 638{ 639 struct genfb_softc *sc = cookie; 640 struct genfb_private *scp = sc->sc_private; 641 struct rasops_info *ri = &scr->scr_ri; 642 int wantcols; 643 bool is_bgr, is_swapped, is_10bit; 644 645 ri->ri_depth = sc->sc_depth; 646 ri->ri_width = sc->sc_width; 647 ri->ri_height = sc->sc_height; 648 ri->ri_stride = sc->sc_stride; 649 ri->ri_flg = RI_CENTER; 650 if (scp->sc_want_clear) 651 ri->ri_flg |= RI_FULLCLEAR; 652 653 scr->scr_flags |= VCONS_LOADFONT; 654 655 if (scp->sc_shadowfb != NULL) { 656 ri->ri_hwbits = (char *)sc->sc_fbaddr; 657 ri->ri_bits = (char *)scp->sc_shadowfb; 658 } else { 659 ri->ri_bits = (char *)sc->sc_fbaddr; 660 scr->scr_flags |= VCONS_DONT_READ; 661 } 662 663 if (existing && scp->sc_want_clear) 664 ri->ri_flg |= RI_CLEAR; 665 666 switch (ri->ri_depth) { 667 case 32: 668 case 24: 669 ri->ri_flg |= RI_ENABLE_ALPHA; 670 671 is_bgr = false; 672 prop_dictionary_get_bool(device_properties(sc->sc_dev), 673 "is_bgr", &is_bgr); 674 675 is_swapped = false; 676 prop_dictionary_get_bool(device_properties(sc->sc_dev), 677 "is_swapped", &is_swapped); 678 679 is_10bit = false; 680 prop_dictionary_get_bool(device_properties(sc->sc_dev), 681 "is_10bit", &is_10bit); 682 683 const int bits = is_10bit ? 10 : 8; 684 ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = bits; 685 686 if (is_bgr) { 687 /* someone requested BGR */ 688 ri->ri_rpos = bits * 0; 689 ri->ri_gpos = bits * 1; 690 ri->ri_bpos = bits * 2; 691 } else if (is_swapped) { 692 /* byte-swapped, must be 32 bpp */ 693 KASSERT(ri->ri_depth == 32); 694 KASSERT(bits == 8); 695 ri->ri_rpos = 8; 696 ri->ri_gpos = 16; 697 ri->ri_bpos = 24; 698 } else { 699 /* assume RGB */ 700 ri->ri_rpos = bits * 2; 701 ri->ri_gpos = bits * 1; 702 ri->ri_bpos = bits * 0; 703 } 704 break; 705 706 case 16: 707 case 15: 708 ri->ri_flg |= RI_ENABLE_ALPHA; 709 break; 710 711 case 8: 712 if (scp->sc_cmcb != NULL) 713 ri->ri_flg |= RI_ENABLE_ALPHA | RI_8BIT_IS_RGB; 714 break; 715 716 case 2: 717 ri->ri_flg |= RI_ENABLE_ALPHA; 718 break; 719 720 default: 721 break; 722 } 723 724 wantcols = genfb_calc_cols(sc, ri); 725 726 rasops_init(ri, 0, wantcols); 727 ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE | 728 WSSCREEN_RESIZE; 729 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 730 sc->sc_width / ri->ri_font->fontwidth); 731 732 if (scp->sc_devcmap != NULL) { 733 memcpy(ri->ri_devcmap, scp->sc_devcmap, sizeof(ri->ri_devcmap)); 734 } 735 736 ri->ri_hw = scr; 737#if GENFB_GLYPHCACHE > 0 738 scp->sc_putchar = ri->ri_ops.putchar; 739 ri->ri_ops.putchar = genfb_putchar; 740#endif 741#ifdef GENFB_DISABLE_TEXT 742 if (scr == &scp->sc_console_screen && !DISABLESPLASH) 743 SCREEN_DISABLE_DRAWING(&scp->sc_console_screen); 744#endif 745} 746 747/* Returns the width of the display in millimeters, or 0 if not known. */ 748static int 749genfb_calc_hsize(struct genfb_softc *sc) 750{ 751 device_t dev = sc->sc_dev; 752 prop_dictionary_t dict = device_properties(dev); 753 prop_data_t edid_data; 754 struct edid_info *edid; 755 const char *edid_ptr; 756 int hsize; 757 758 edid_data = prop_dictionary_get(dict, "EDID"); 759 if (edid_data == NULL || prop_data_size(edid_data) < 128) 760 return 0; 761 762 edid = kmem_alloc(sizeof(*edid), KM_SLEEP); 763 764 edid_ptr = prop_data_value(edid_data); 765 if (edid_parse(__UNCONST(edid_ptr), edid) == 0) 766 hsize = (int)edid->edid_max_hsize * 10; 767 else 768 hsize = 0; 769 770 kmem_free(edid, sizeof(*edid)); 771 772 return hsize; 773} 774 775/* Return the minimum number of character columns based on DPI */ 776static int 777genfb_calc_cols(struct genfb_softc *sc, struct rasops_info *ri) 778{ 779 const int hsize = genfb_calc_hsize(sc); 780 781 if (hsize != 0) { 782 ri->ri_flg |= RI_PREFER_WIDEFONT; 783 } 784 785 return MAX(RASOPS_DEFAULT_WIDTH, hsize / GENFB_CHAR_WIDTH_MM); 786} 787 788static int 789genfb_putcmap(struct genfb_softc *sc, struct wsdisplay_cmap *cm) 790{ 791 struct genfb_private *scp = sc->sc_private; 792 u_char *r, *g, *b; 793 u_int index = cm->index; 794 u_int count = cm->count; 795 int i, error; 796 u_char rbuf[256], gbuf[256], bbuf[256]; 797 798#ifdef GENFB_DEBUG 799 aprint_debug("putcmap: %d %d\n",index, count); 800#endif 801 if (index >= 256 || count > 256 || index + count > 256) 802 return EINVAL; 803 804 error = copyin(cm->red, &rbuf[index], count); 805 if (error) 806 return error; 807 error = copyin(cm->green, &gbuf[index], count); 808 if (error) 809 return error; 810 error = copyin(cm->blue, &bbuf[index], count); 811 if (error) 812 return error; 813 814 memcpy(&scp->sc_cmap_red[index], &rbuf[index], count); 815 memcpy(&scp->sc_cmap_green[index], &gbuf[index], count); 816 memcpy(&scp->sc_cmap_blue[index], &bbuf[index], count); 817 818 r = &scp->sc_cmap_red[index]; 819 g = &scp->sc_cmap_green[index]; 820 b = &scp->sc_cmap_blue[index]; 821 822 for (i = 0; i < count; i++) { 823 genfb_putpalreg(sc, index, *r, *g, *b); 824 index++; 825 r++, g++, b++; 826 } 827 return 0; 828} 829 830static int 831genfb_getcmap(struct genfb_softc *sc, struct wsdisplay_cmap *cm) 832{ 833 struct genfb_private *scp = sc->sc_private; 834 u_int index = cm->index; 835 u_int count = cm->count; 836 int error; 837 838 if (index >= 256 || count > 256 || index + count > 256) 839 return EINVAL; 840 841 error = copyout(&scp->sc_cmap_red[index], cm->red, count); 842 if (error) 843 return error; 844 error = copyout(&scp->sc_cmap_green[index], cm->green, count); 845 if (error) 846 return error; 847 error = copyout(&scp->sc_cmap_blue[index], cm->blue, count); 848 if (error) 849 return error; 850 851 return 0; 852} 853 854void 855genfb_restore_palette(struct genfb_softc *sc) 856{ 857 struct genfb_private *scp = sc->sc_private; 858 int i; 859 860 if (sc->sc_depth <= 8) { 861 for (i = 0; i < (1 << sc->sc_depth); i++) { 862 genfb_putpalreg(sc, i, scp->sc_cmap_red[i], 863 scp->sc_cmap_green[i], scp->sc_cmap_blue[i]); 864 } 865 } 866} 867 868static void 869genfb_init_palette(struct genfb_softc *sc) 870{ 871 struct genfb_private *scp = sc->sc_private; 872 int i, j, tmp; 873 874 if (sc->sc_depth == 8) { 875 /* generate an r3g3b2 colour map */ 876 for (i = 0; i < 256; i++) { 877 tmp = i & 0xe0; 878 /* 879 * replicate bits so 0xe0 maps to a red value of 0xff 880 * in order to make white look actually white 881 */ 882 tmp |= (tmp >> 3) | (tmp >> 6); 883 scp->sc_cmap_red[i] = tmp; 884 885 tmp = (i & 0x1c) << 3; 886 tmp |= (tmp >> 3) | (tmp >> 6); 887 scp->sc_cmap_green[i] = tmp; 888 889 tmp = (i & 0x03) << 6; 890 tmp |= tmp >> 2; 891 tmp |= tmp >> 4; 892 scp->sc_cmap_blue[i] = tmp; 893 894 genfb_putpalreg(sc, i, scp->sc_cmap_red[i], 895 scp->sc_cmap_green[i], 896 scp->sc_cmap_blue[i]); 897 } 898 } else { 899 /* steal rasops' ANSI cmap */ 900 j = 0; 901 for (i = 0; i < 256; i++) { 902 scp->sc_cmap_red[i] = rasops_cmap[j]; 903 scp->sc_cmap_green[i] = rasops_cmap[j + 1]; 904 scp->sc_cmap_blue[i] = rasops_cmap[j + 2]; 905 j += 3; 906 } 907 } 908} 909 910static int 911genfb_putpalreg(struct genfb_softc *sc, uint8_t idx, uint8_t r, uint8_t g, 912 uint8_t b) 913{ 914 struct genfb_private *scp = sc->sc_private; 915 916 if (scp->sc_cmcb) { 917 scp->sc_cmcb->gcc_set_mapreg(scp->sc_cmcb->gcc_cookie, 918 idx, r, g, b); 919 } 920 return 0; 921} 922 923void 924genfb_cnattach(void) 925{ 926 genfb_cnattach_called = 1; 927} 928 929int 930genfb_cndetach(void) 931{ 932 933 if (genfb_cnattach_called) { 934 genfb_cnattach_called = 0; 935 return 1; 936 } 937 return 0; 938} 939 940void 941genfb_disable(void) 942{ 943 genfb_enabled = 0; 944} 945 946int 947genfb_is_console(void) 948{ 949 return genfb_cnattach_called; 950} 951 952int 953genfb_is_enabled(void) 954{ 955 return genfb_enabled; 956} 957 958int 959genfb_borrow(bus_addr_t addr, bus_space_handle_t *hdlp) 960{ 961 struct genfb_softc *sc = genfb_softc; 962 963 if (sc && sc->sc_private && sc->sc_private->sc_ops.genfb_borrow) 964 return sc->sc_private->sc_ops.genfb_borrow(sc, addr, hdlp); 965 return 0; 966} 967 968static void 969genfb_brightness_up(device_t dev) 970{ 971 struct genfb_softc *sc = device_private(dev); 972 struct genfb_private *scp = sc->sc_private; 973 974 KASSERT(scp->sc_brightness != NULL); 975 KASSERT(scp->sc_brightness->gpc_upd_parameter != NULL); 976 977 (void)scp->sc_brightness->gpc_upd_parameter( 978 scp->sc_brightness->gpc_cookie, GENFB_BRIGHTNESS_STEP); 979} 980 981static void 982genfb_brightness_down(device_t dev) 983{ 984 struct genfb_softc *sc = device_private(dev); 985 struct genfb_private *scp = sc->sc_private; 986 987 KASSERT(scp->sc_brightness != NULL); 988 KASSERT(scp->sc_brightness->gpc_upd_parameter != NULL); 989 990 (void)scp->sc_brightness->gpc_upd_parameter( 991 scp->sc_brightness->gpc_cookie, -GENFB_BRIGHTNESS_STEP); 992} 993 994void 995genfb_enable_polling(device_t dev) 996{ 997 struct genfb_softc *sc = device_private(dev); 998 struct genfb_private *scp = sc->sc_private; 999 1000 if (scp == NULL) 1001 return; 1002 1003 if (scp->sc_console_screen.scr_vd) { 1004 SCREEN_ENABLE_DRAWING(&scp->sc_console_screen); 1005 vcons_hard_switch(&scp->sc_console_screen); 1006 vcons_enable_polling(&sc->vd); 1007 if (scp->sc_ops.genfb_enable_polling) 1008 (*scp->sc_ops.genfb_enable_polling)(sc); 1009 } 1010} 1011 1012void 1013genfb_disable_polling(device_t dev) 1014{ 1015 struct genfb_softc *sc = device_private(dev); 1016 struct genfb_private *scp = sc->sc_private; 1017 1018 if (scp == NULL) 1019 return; 1020 1021 if (scp->sc_console_screen.scr_vd) { 1022 if (scp->sc_ops.genfb_disable_polling) 1023 (*scp->sc_ops.genfb_disable_polling)(sc); 1024 vcons_disable_polling(&sc->vd); 1025 } 1026} 1027 1028#if GENFB_GLYPHCACHE > 0 1029#define GLYPHCACHESIZE ((GENFB_GLYPHCACHE) * 1024 * 1024) 1030 1031static inline int 1032attr2idx(long attr) 1033{ 1034 if ((attr & 0xf0f00ff8) != 0) 1035 return -1; 1036 1037 return (((attr >> 16) & 0x0f) | ((attr >> 20) & 0xf0)); 1038} 1039 1040static int 1041genfb_setup_glyphcache(struct genfb_softc *sc, long defattr) 1042{ 1043 struct genfb_private *scp = sc->sc_private; 1044 struct rasops_info *ri = &scp->sc_console_screen.scr_ri, 1045 *cri = &scp->sc_cache_ri; 1046 gc_bucket *b; 1047 int i, usedcells = 0, idx, j; 1048 1049 scp->sc_cache = kmem_alloc(GLYPHCACHESIZE, KM_SLEEP); 1050 1051 /* 1052 * now we build a mutant rasops_info for the cache - same pixel type 1053 * and such as the real fb, but only one character per line for 1054 * simplicity and locality 1055 */ 1056 memcpy(cri, ri, sizeof(struct rasops_info)); 1057 cri->ri_ops.putchar = scp->sc_putchar; 1058 cri->ri_width = ri->ri_font->fontwidth; 1059 cri->ri_stride = ri->ri_xscale; 1060 cri->ri_bits = scp->sc_cache; 1061 cri->ri_hwbits = NULL; 1062 cri->ri_origbits = scp->sc_cache; 1063 cri->ri_cols = 1; 1064 cri->ri_rows = GLYPHCACHESIZE / 1065 (cri->ri_stride * cri->ri_font->fontheight); 1066 cri->ri_xorigin = 0; 1067 cri->ri_yorigin = 0; 1068 cri->ri_xscale = ri->ri_xscale; 1069 cri->ri_yscale = ri->ri_font->fontheight * ri->ri_xscale; 1070 1071 printf("size %d %d %d\n", GLYPHCACHESIZE, ri->ri_width, ri->ri_stride); 1072 printf("cells: %d\n", cri->ri_rows); 1073 scp->sc_nbuckets = uimin(256, cri->ri_rows / 223); 1074 scp->sc_buckets = kmem_alloc(sizeof(gc_bucket) * scp->sc_nbuckets, 1075 KM_SLEEP); 1076 printf("buckets: %d\n", scp->sc_nbuckets); 1077 for (i = 0; i < scp->sc_nbuckets; i++) { 1078 b = &scp->sc_buckets[i]; 1079 b->gb_firstcell = usedcells; 1080 b->gb_numcells = uimin(223, cri->ri_rows - usedcells); 1081 usedcells += 223; 1082 b->gb_usedcells = 0; 1083 b->gb_index = -1; 1084 for (j = 0; j < 223; j++) b->gb_map[j] = -1; 1085 } 1086 1087 /* initialize the attribute map... */ 1088 for (i = 0; i < 256; i++) { 1089 scp->sc_attrmap[i] = -1; 1090 } 1091 1092 /* first bucket goes to default attr */ 1093 idx = attr2idx(defattr); 1094 printf("defattr %08lx idx %x\n", defattr, idx); 1095 1096 if (idx >= 0) { 1097 scp->sc_attrmap[idx] = 0; 1098 scp->sc_buckets[0].gb_index = idx; 1099 } 1100 1101 return 0; 1102} 1103 1104static void 1105genfb_putchar(void *cookie, int row, int col, u_int c, long attr) 1106{ 1107 struct rasops_info *ri = cookie; 1108 struct vcons_screen *scr = ri->ri_hw; 1109 struct genfb_softc *sc = scr->scr_cookie; 1110 struct genfb_private *scp = sc->sc_private; 1111 uint8_t *src, *dst, *hwdst; 1112 gc_bucket *b; 1113 int i, idx, bi, cell; 1114 1115 attr &= ~WSATTR_USERMASK; 1116 1117 idx = attr2idx(attr); 1118 if (c < 33 || c > 255 || idx < 0) goto nope; 1119 1120 /* look for a bucket with the right attribute */ 1121 bi = scp->sc_attrmap[idx]; 1122 if (bi == -1) { 1123 /* nope, see if there's an empty one left */ 1124 bi = 1; 1125 while ((bi < scp->sc_nbuckets) && 1126 (scp->sc_buckets[bi].gb_index != -1)) { 1127 bi++; 1128 } 1129 if (bi < scp->sc_nbuckets) { 1130 /* found one -> grab it */ 1131 scp->sc_attrmap[idx] = bi; 1132 b = &scp->sc_buckets[bi]; 1133 b->gb_index = idx; 1134 b->gb_usedcells = 0; 1135 /* make sure this doesn't get evicted right away */ 1136 b->gb_lastread = time_uptime; 1137 } else { 1138 /* 1139 * still nothing 1140 * steal the least recently read bucket 1141 */ 1142 time_t moo = time_uptime; 1143 int oldest = 1; 1144 1145 for (i = 1; i < scp->sc_nbuckets; i++) { 1146 if (scp->sc_buckets[i].gb_lastread < moo) { 1147 oldest = i; 1148 moo = scp->sc_buckets[i].gb_lastread; 1149 } 1150 } 1151 1152 /* if we end up here all buckets must be in use */ 1153 b = &scp->sc_buckets[oldest]; 1154 scp->sc_attrmap[b->gb_index] = -1; 1155 b->gb_index = idx; 1156 b->gb_usedcells = 0; 1157 scp->sc_attrmap[idx] = oldest; 1158 /* now scrub it */ 1159 for (i = 0; i < 223; i++) 1160 b->gb_map[i] = -1; 1161 /* and set the time stamp */ 1162 b->gb_lastread = time_uptime; 1163 } 1164 } else { 1165 /* found one */ 1166 b = &scp->sc_buckets[bi]; 1167 } 1168 1169 /* see if there's room in the bucket */ 1170 if (b->gb_usedcells >= b->gb_numcells) goto nope; 1171 1172 cell = b->gb_map[c - 33]; 1173 if (cell == -1) { 1174 if (b->gb_usedcells >= b->gb_numcells) 1175 goto nope; 1176 cell = atomic_add_int_nv(&b->gb_usedcells, 1) - 1; 1177 b->gb_map[c - 33] = cell; 1178 cell += b->gb_firstcell; 1179 scp->sc_putchar(&scp->sc_cache_ri, cell, 0, c, attr); 1180 } else 1181 cell += b->gb_firstcell; 1182 1183 src = scp->sc_cache + cell * scp->sc_cache_ri.ri_yscale; 1184 dst = ri->ri_bits + row * ri->ri_yscale + col * ri->ri_xscale; 1185 for (i = 0; i < ri->ri_font->fontheight; i++) { 1186 memcpy(dst, src, ri->ri_xscale); 1187 src += ri->ri_xscale; 1188 dst += ri->ri_stride; 1189 } 1190 if (ri->ri_hwbits) { 1191 src = scp->sc_cache + cell * scp->sc_cache_ri.ri_yscale; 1192 hwdst = ri->ri_hwbits 1193 + row * ri->ri_yscale + col * ri->ri_xscale; 1194 for (i = 0; i < ri->ri_font->fontheight; i++) { 1195 memcpy(hwdst, src, ri->ri_xscale); 1196 src += ri->ri_xscale; 1197 hwdst += ri->ri_stride; 1198 } 1199 } 1200 b->gb_lastread = time_uptime; 1201 return; 1202nope: 1203 scp->sc_putchar(cookie, row, col, c, attr); 1204} 1205 1206#endif 1207