1 /* $NetBSD: ssdfb.c,v 1.13 2021/04/24 23:36:55 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2019 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tobias Nygren. 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 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: ssdfb.c,v 1.13 2021/04/24 23:36:55 thorpej Exp $"); 34 35 #include "opt_ddb.h" 36 37 #include <sys/param.h> 38 #include <sys/kernel.h> 39 #include <sys/conf.h> 40 #include <sys/condvar.h> 41 #include <sys/kmem.h> 42 #include <sys/kthread.h> 43 44 #include <uvm/uvm_device.h> 45 #include <uvm/uvm_extern.h> 46 47 #include <dev/wscons/wsdisplayvar.h> 48 #include <dev/rasops/rasops.h> 49 #include <dev/ic/ssdfbvar.h> 50 51 #if defined(DDB) 52 #include <machine/db_machdep.h> 53 #include <ddb/db_extern.h> 54 #endif 55 56 /* userland interface */ 57 static int ssdfb_ioctl(void *, void *, u_long, void *, int, struct lwp *); 58 static paddr_t ssdfb_mmap(void *, void *, off_t, int); 59 60 /* wscons screen management */ 61 static int ssdfb_alloc_screen(void *, const struct wsscreen_descr *, 62 void **, int *, int *, long *); 63 static void ssdfb_free_screen(void *, void *); 64 static int ssdfb_show_screen(void *, void *, int, 65 void (*cb) (void *, int, int), void *); 66 67 /* rasops hooks */ 68 static void ssdfb_putchar(void *, int, int, u_int, long); 69 static void ssdfb_copycols(void *, int, int, int, int); 70 static void ssdfb_erasecols(void *, int, int, int, long); 71 static void ssdfb_copyrows(void *, int, int, int); 72 static void ssdfb_eraserows(void *, int, int, long); 73 static void ssdfb_cursor(void *, int, int, int); 74 75 /* hardware interface */ 76 static int ssdfb_init_ssd1306(struct ssdfb_softc *); 77 static int ssdfb_init_ssd1322(struct ssdfb_softc *); 78 static int ssdfb_set_contrast(struct ssdfb_softc *, uint8_t, bool); 79 static int ssdfb_set_display_on(struct ssdfb_softc *, bool, bool); 80 static int ssdfb_set_mode(struct ssdfb_softc *, u_int); 81 82 /* frame buffer damage tracking and synchronization */ 83 static void ssdfb_udv_attach(struct ssdfb_softc *sc); 84 static bool ssdfb_is_modified(struct ssdfb_softc *sc); 85 static bool ssdfb_clear_modify(struct ssdfb_softc *sc); 86 static void ssdfb_damage(struct ssdfb_softc *); 87 static void ssdfb_thread(void *); 88 static void ssdfb_set_usepoll(struct ssdfb_softc *, bool); 89 static int ssdfb_sync(struct ssdfb_softc *, bool); 90 static int ssdfb_sync_ssd1306(struct ssdfb_softc *, bool); 91 static int ssdfb_sync_ssd1322(struct ssdfb_softc *, bool); 92 static uint64_t ssdfb_transpose_block(uint8_t *, size_t); 93 94 /* misc helpers */ 95 static const struct ssdfb_product * 96 ssdfb_lookup_product(ssdfb_product_id_t); 97 static int ssdfb_pick_font(int *, struct wsdisplay_font **); 98 static void ssdfb_clear_screen(struct ssdfb_softc *); 99 #if defined(DDB) 100 static void ssdfb_ddb_trap_callback(int); 101 #endif 102 103 static const char *ssdfb_controller_names[] = { 104 [SSDFB_CONTROLLER_UNKNOWN] = "unknown", 105 [SSDFB_CONTROLLER_SSD1306] = "Solomon Systech SSD1306", 106 [SSDFB_CONTROLLER_SH1106] = "Sino Wealth SH1106", 107 [SSDFB_CONTROLLER_SSD1322] = "Solomon Systech SSD1322" 108 }; 109 110 /* 111 * Display module assemblies supported by this driver. 112 */ 113 static const struct ssdfb_product ssdfb_products[] = { 114 { 115 .p_product_id = SSDFB_PRODUCT_SSD1306_GENERIC, 116 .p_controller_id = SSDFB_CONTROLLER_SSD1306, 117 .p_name = "generic", 118 .p_width = 128, 119 .p_height = 64, 120 .p_bits_per_pixel = 1, 121 .p_panel_shift = 0, 122 .p_fosc = 0x8, 123 .p_fosc_div = 0, 124 .p_precharge = 0x1, 125 .p_discharge = 0xf, 126 .p_compin_cfg = SSDFB_COM_PINS_A1_MASK 127 | SSDFB_COM_PINS_ALTERNATIVE_MASK, 128 .p_vcomh_deselect_level = SSD1306_VCOMH_DESELECT_LEVEL_0_77_VCC, 129 .p_default_contrast = 0x7f, 130 .p_multiplex_ratio = 0x3f, 131 .p_init = ssdfb_init_ssd1306, 132 .p_sync = ssdfb_sync_ssd1306 133 }, 134 { 135 .p_product_id = SSDFB_PRODUCT_SH1106_GENERIC, 136 .p_controller_id = SSDFB_CONTROLLER_SH1106, 137 .p_name = "generic", 138 .p_width = 128, 139 .p_height = 64, 140 .p_bits_per_pixel = 1, 141 .p_panel_shift = 2, 142 .p_fosc = 0x5, 143 .p_fosc_div = 0, 144 .p_precharge = 0x2, 145 .p_discharge = 0x2, 146 .p_compin_cfg = SSDFB_COM_PINS_A1_MASK 147 | SSDFB_COM_PINS_ALTERNATIVE_MASK, 148 .p_vcomh_deselect_level = SH1106_VCOMH_DESELECT_LEVEL_DEFAULT, 149 .p_default_contrast = 0x80, 150 .p_multiplex_ratio = 0x3f, 151 .p_init = ssdfb_init_ssd1306, 152 .p_sync = ssdfb_sync_ssd1306 153 }, 154 { 155 .p_product_id = SSDFB_PRODUCT_ADAFRUIT_938, 156 .p_controller_id = SSDFB_CONTROLLER_SSD1306, 157 .p_name = "Adafruit Industries, LLC product 938", 158 .p_width = 128, 159 .p_height = 64, 160 .p_bits_per_pixel = 1, 161 .p_panel_shift = 0, 162 .p_fosc = 0x8, 163 .p_fosc_div = 0, 164 .p_precharge = 0x1, 165 .p_discharge = 0xf, 166 .p_compin_cfg = 0x12, 167 .p_vcomh_deselect_level = 0x40, 168 .p_default_contrast = 0x8f, 169 .p_multiplex_ratio = 0x3f, 170 .p_init = ssdfb_init_ssd1306, 171 .p_sync = ssdfb_sync_ssd1306 172 }, 173 { 174 .p_product_id = SSDFB_PRODUCT_ADAFRUIT_931, 175 .p_controller_id = SSDFB_CONTROLLER_SSD1306, 176 .p_name = "Adafruit Industries, LLC product 931", 177 .p_width = 128, 178 .p_height = 32, 179 .p_bits_per_pixel = 1, 180 .p_panel_shift = 0, 181 .p_fosc = 0x8, 182 .p_fosc_div = 0, 183 .p_precharge = 0x1, 184 .p_discharge = 0xf, 185 .p_compin_cfg = 0x2, 186 .p_vcomh_deselect_level = 0x40, 187 .p_default_contrast = 0x8f, 188 .p_multiplex_ratio = 0x1f, 189 .p_init = ssdfb_init_ssd1306, 190 .p_sync = ssdfb_sync_ssd1306 191 }, 192 { 193 .p_product_id = SSDFB_PRODUCT_SSD1322_GENERIC, 194 .p_controller_id = SSDFB_CONTROLLER_SSD1322, 195 .p_name = "generic", 196 .p_width = 256, 197 .p_height = 64, 198 .p_bits_per_pixel = 4, 199 .p_panel_shift = 28, 200 .p_vcomh_deselect_level = SSD1322_DEFAULT_VCOMH, 201 .p_fosc = SSD1322_DEFAULT_FREQUENCY, 202 .p_fosc_div = SSD1322_DEFAULT_DIVIDER, 203 .p_default_contrast = SSD1322_DEFAULT_CONTRAST_CURRENT, 204 .p_multiplex_ratio = 0x3f, 205 .p_init = ssdfb_init_ssd1322, 206 .p_sync = ssdfb_sync_ssd1322 207 } 208 }; 209 210 static const struct wsdisplay_accessops ssdfb_accessops = { 211 .ioctl = ssdfb_ioctl, 212 .mmap = ssdfb_mmap, 213 .alloc_screen = ssdfb_alloc_screen, 214 .free_screen = ssdfb_free_screen, 215 .show_screen = ssdfb_show_screen 216 }; 217 218 #define SSDFB_CMD1(c) do { cmd[0] = (c); error = sc->sc_cmd(sc->sc_cookie, cmd, 1, usepoll); } while(0) 219 #define SSDFB_CMD2(c, a) do { cmd[0] = (c); cmd[1] = (a); error = sc->sc_cmd(sc->sc_cookie, cmd, 2, usepoll); } while(0) 220 #define SSDFB_CMD3(c, a, b) do { cmd[0] = (c); cmd[1] = (a); cmd[2] = (b); error = sc->sc_cmd(sc->sc_cookie, cmd, 3, usepoll); } while(0) 221 222 void 223 ssdfb_attach(struct ssdfb_softc *sc, int flags) 224 { 225 struct wsemuldisplaydev_attach_args aa; 226 struct rasops_info *ri = &sc->sc_ri; 227 int error = 0; 228 long defattr; 229 const struct ssdfb_product *p; 230 int kt_flags; 231 232 p = ssdfb_lookup_product(flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK); 233 if (p == NULL) { 234 aprint_error(": unknown display assembly\n"); 235 return; 236 } 237 sc->sc_p = p; 238 239 aprint_naive("\n"); 240 aprint_normal(": %s (%s)\n", 241 ssdfb_controller_names[p->p_controller_id], 242 p->p_name); 243 244 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 245 sc->sc_is_console = flags & SSDFB_ATTACH_FLAG_CONSOLE ? true : false; 246 sc->sc_inverse = flags & SSDFB_ATTACH_FLAG_INVERSE ? true : false; 247 sc->sc_upsidedown = flags & SSDFB_ATTACH_FLAG_UPSIDEDOWN ? true : false; 248 sc->sc_backoff = 1; 249 sc->sc_contrast = sc->sc_p->p_default_contrast; 250 sc->sc_gddram_len = sc->sc_p->p_width * sc->sc_p->p_height 251 * sc->sc_p->p_bits_per_pixel / 8; 252 sc->sc_gddram = kmem_alloc(sc->sc_gddram_len, KM_SLEEP); 253 if (sc->sc_gddram == NULL) 254 goto out; 255 256 aprint_normal_dev(sc->sc_dev, "%dx%d%s\n", sc->sc_p->p_width, 257 sc->sc_p->p_height, sc->sc_is_console ? ", console" : ""); 258 259 /* 260 * Initialize rasops. The native depth is 1-bit monochrome and we 261 * support this in text emul mode via rasops1. But modern Xorg 262 * userland has many rendering glitches when running with 1-bit depth 263 * so to better support this use case we instead declare ourselves as 264 * an 8-bit display with a two entry constant color map. 265 */ 266 error = ssdfb_pick_font(&sc->sc_fontcookie, &sc->sc_font); 267 if (error) { 268 aprint_error_dev(sc->sc_dev, "no font\n"); 269 goto out; 270 } 271 #ifdef SSDFB_USE_NATIVE_DEPTH 272 ri->ri_depth = sc->sc_p->p_bits_per_pixel; 273 #else 274 ri->ri_depth = 8; 275 #endif 276 ri->ri_font = sc->sc_font; 277 ri->ri_width = sc->sc_p->p_width; 278 ri->ri_height = sc->sc_p->p_height; 279 ri->ri_stride = ri->ri_width * ri->ri_depth / 8; 280 ri->ri_hw = sc; 281 ri->ri_flg = RI_FULLCLEAR | RI_FORCEMONO; 282 sc->sc_ri_bits_len = round_page(ri->ri_stride * ri->ri_height); 283 ri->ri_bits = (u_char *)uvm_km_alloc(kernel_map, sc->sc_ri_bits_len, 284 0, UVM_KMF_WIRED); 285 if (ri->ri_bits == NULL) 286 goto out; 287 288 error = rasops_init(ri, 289 sc->sc_p->p_height / sc->sc_font->fontheight, 290 sc->sc_p->p_width / sc->sc_font->fontwidth); 291 if (error) 292 goto out; 293 294 ri->ri_caps &= ~WSSCREEN_WSCOLORS; 295 296 /* 297 * Save original emul ops & insert our damage notification hooks. 298 */ 299 sc->sc_orig_riops = ri->ri_ops; 300 ri->ri_ops.putchar = ssdfb_putchar; 301 ri->ri_ops.copycols = ssdfb_copycols; 302 ri->ri_ops.erasecols = ssdfb_erasecols; 303 ri->ri_ops.copyrows = ssdfb_copyrows; 304 ri->ri_ops.eraserows = ssdfb_eraserows; 305 ri->ri_ops.cursor = ssdfb_cursor; 306 307 /* 308 * Set up the screen. 309 */ 310 sc->sc_screen_descr = (struct wsscreen_descr){ 311 .name = "default", 312 .ncols = ri->ri_cols, 313 .nrows = ri->ri_rows, 314 .textops = &ri->ri_ops, 315 .fontwidth = ri->ri_font->fontwidth, 316 .fontheight = ri->ri_font->fontheight, 317 .capabilities = ri->ri_caps 318 }; 319 sc->sc_screens[0] = &sc->sc_screen_descr; 320 sc->sc_screenlist = (struct wsscreen_list){ 321 .nscreens = 1, 322 .screens = sc->sc_screens 323 }; 324 325 /* 326 * Initialize hardware. 327 */ 328 error = p->p_init(sc); 329 if (error) 330 goto out; 331 332 if (sc->sc_is_console) 333 ssdfb_set_usepoll(sc, true); 334 335 mutex_init(&sc->sc_cond_mtx, MUTEX_DEFAULT, 336 ISSET(flags, SSDFB_ATTACH_FLAG_MPSAFE) ? IPL_SCHED : IPL_BIO); 337 cv_init(&sc->sc_cond, "ssdfb"); 338 kt_flags = KTHREAD_MUSTJOIN; 339 /* XXX spi(4) is not MPSAFE yet. */ 340 if (ISSET(flags, SSDFB_ATTACH_FLAG_MPSAFE)) 341 kt_flags |= KTHREAD_MPSAFE; 342 error = kthread_create(PRI_SOFTCLOCK, kt_flags, NULL, ssdfb_thread, sc, 343 &sc->sc_thread, "%s", device_xname(sc->sc_dev)); 344 if (error) { 345 cv_destroy(&sc->sc_cond); 346 mutex_destroy(&sc->sc_cond_mtx); 347 goto out; 348 } 349 350 /* 351 * Attach wsdisplay. 352 */ 353 if (sc->sc_is_console) { 354 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 355 wsdisplay_cnattach(&sc->sc_screen_descr, ri, 0, 0, defattr); 356 #if defined(DDB) 357 db_trap_callback = ssdfb_ddb_trap_callback; 358 #endif 359 } 360 aa = (struct wsemuldisplaydev_attach_args){ 361 .console = sc->sc_is_console, 362 .scrdata = &sc->sc_screenlist, 363 .accessops = &ssdfb_accessops, 364 .accesscookie = sc 365 }; 366 sc->sc_wsdisplay = 367 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint, CFARG_EOL); 368 369 return; 370 out: 371 aprint_error_dev(sc->sc_dev, "attach failed: %d\n", error); 372 if (sc->sc_gddram != NULL) 373 kmem_free(sc->sc_gddram, sc->sc_gddram_len); 374 if (ri->ri_bits != NULL) 375 uvm_km_free(kernel_map, (vaddr_t)ri->ri_bits, sc->sc_ri_bits_len, 376 UVM_KMF_WIRED); 377 if (sc->sc_fontcookie > 0) 378 (void) wsfont_unlock(sc->sc_fontcookie); 379 } 380 381 int 382 ssdfb_detach(struct ssdfb_softc *sc) 383 { 384 mutex_enter(&sc->sc_cond_mtx); 385 sc->sc_detaching = true; 386 cv_broadcast(&sc->sc_cond); 387 mutex_exit(&sc->sc_cond_mtx); 388 kthread_join(sc->sc_thread); 389 390 if (sc->sc_uobj != NULL) { 391 rw_enter(sc->sc_uobj->vmobjlock, RW_WRITER); 392 sc->sc_uobj->uo_refs--; 393 rw_exit(sc->sc_uobj->vmobjlock); 394 } 395 config_detach(sc->sc_wsdisplay, DETACH_FORCE); 396 397 cv_destroy(&sc->sc_cond); 398 mutex_destroy(&sc->sc_cond_mtx); 399 uvm_km_free(kernel_map, (vaddr_t)sc->sc_ri.ri_bits, sc->sc_ri_bits_len, 400 UVM_KMF_WIRED); 401 kmem_free(sc->sc_gddram, sc->sc_gddram_len); 402 (void) wsfont_unlock(sc->sc_fontcookie); 403 return 0; 404 } 405 406 static int 407 ssdfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) 408 { 409 struct ssdfb_softc *sc = v; 410 struct wsdisplay_param *wdp; 411 struct wsdisplay_cmap *wc; 412 u_char cmap[16]; 413 int cmaplen = 1 << sc->sc_p->p_bits_per_pixel; 414 int i; 415 struct wsdisplayio_fbinfo *fbi; 416 int error; 417 418 switch (cmd) { 419 case WSDISPLAYIO_GTYPE: 420 *(u_int *)data = WSDISPLAY_TYPE_SSDFB; 421 return 0; 422 case WSDISPLAYIO_GINFO: 423 *(struct wsdisplay_fbinfo *)data = (struct wsdisplay_fbinfo){ 424 .width = sc->sc_ri.ri_width, 425 .height = sc->sc_ri.ri_height, 426 .depth = sc->sc_ri.ri_depth, 427 .cmsize = cmaplen 428 }; 429 return 0; 430 case WSDISPLAYIO_GET_FBINFO: 431 fbi = (struct wsdisplayio_fbinfo *)data; 432 error = wsdisplayio_get_fbinfo(&sc->sc_ri, fbi); 433 fbi->fbi_subtype.fbi_cmapinfo.cmap_entries = cmaplen; 434 /* fbi->fbi_pixeltype = WSFB_GREYSCALE */; 435 return error; 436 case WSDISPLAYIO_LINEBYTES: 437 *(u_int *)data = sc->sc_ri.ri_stride; 438 return 0; 439 case WSDISPLAYIO_GETPARAM: 440 wdp = (struct wsdisplay_param *)data; 441 if (wdp->param != WSDISPLAYIO_PARAM_CONTRAST) 442 return EINVAL; 443 wdp->min = 0; 444 wdp->max = 0xff; 445 wdp->curval = sc->sc_contrast; 446 return 0; 447 case WSDISPLAYIO_SETPARAM: 448 wdp = (struct wsdisplay_param *)data; 449 if (wdp->param != WSDISPLAYIO_PARAM_CONTRAST) 450 return EINVAL; 451 if (wdp->curval < 0 || wdp->curval > 0xff) 452 return EINVAL; 453 return ssdfb_set_contrast(sc, wdp->curval, sc->sc_usepoll); 454 case WSDISPLAYIO_GMODE: 455 *(u_int *)data = sc->sc_mode; 456 return 0; 457 case WSDISPLAYIO_SMODE: 458 return ssdfb_set_mode(sc, *(u_int *)data); 459 case WSDISPLAYIO_GVIDEO: 460 *(u_int *)data = sc->sc_display_on 461 ? WSDISPLAYIO_VIDEO_ON 462 : WSDISPLAYIO_VIDEO_OFF; 463 return 0; 464 case WSDISPLAYIO_SVIDEO: 465 switch (*(u_int *)data) { 466 case WSDISPLAYIO_VIDEO_ON: 467 case WSDISPLAYIO_VIDEO_OFF: 468 break; 469 default: 470 return EINVAL; 471 } 472 return ssdfb_set_display_on(sc, 473 *(u_int *)data == WSDISPLAYIO_VIDEO_ON ? true : false, 474 sc->sc_usepoll); 475 #if 0 /* don't let userland mess with polling yet */ 476 case WSDISPLAYIO_SET_POLLING: 477 switch (*(u_int *)data) { 478 case 0: 479 case 1: 480 break; 481 default: 482 return EINVAL; 483 } 484 mutex_enter(&sc->sc_cond_mtx); 485 ssdfb_set_usepoll(sc, *(u_int *)data ? true : false); 486 cv_broadcast(&sc->sc_cond); 487 mutex_exit(&sc->sc_cond_mtx); 488 return 0; 489 #endif 490 case WSDISPLAYIO_GETCMAP: 491 wc = (struct wsdisplay_cmap *)data; 492 if (wc->index >= cmaplen || 493 wc->count > cmaplen - wc->index) 494 return EINVAL; 495 for(i = 0; i < cmaplen; i++) { 496 cmap[i] = 255 * i / (cmaplen - 1); 497 } 498 error = copyout(&cmap[wc->index], wc->red, wc->count); 499 if (error) 500 return error; 501 error = copyout(&cmap[wc->index], wc->green, wc->count); 502 if (error) 503 return error; 504 error = copyout(&cmap[wc->index], wc->blue, wc->count); 505 return error; 506 case WSDISPLAYIO_PUTCMAP: 507 return ENODEV; 508 } 509 510 return EPASSTHROUGH; 511 } 512 513 static paddr_t 514 ssdfb_mmap(void *v, void *vs, off_t off, int prot) 515 { 516 struct ssdfb_softc *sc = (struct ssdfb_softc *)v; 517 struct rasops_info *ri = &sc->sc_ri; 518 vaddr_t va_base = (vaddr_t)ri->ri_bits; 519 paddr_t pa; 520 521 if (off < 0 || off >= sc->sc_ri_bits_len || (off & PAGE_MASK) != 0) 522 return -1; 523 524 if (!pmap_extract(pmap_kernel(), va_base + off, &pa)) 525 return -1; 526 527 return atop(pa); 528 } 529 530 static int 531 ssdfb_alloc_screen(void *v, const struct wsscreen_descr *descr, void **cookiep, 532 int *curxp, int *curyp, long *attrp) 533 { 534 struct ssdfb_softc *sc = v; 535 struct rasops_info *ri = &sc->sc_ri; 536 537 if (sc->sc_nscreens > 0) 538 return ENOMEM; 539 540 ri->ri_ops.allocattr(ri, 0, 0, 0, attrp); 541 *cookiep = &sc->sc_ri; 542 *curxp = 0; 543 *curyp = 0; 544 sc->sc_nscreens++; 545 546 return 0; 547 } 548 549 static void 550 ssdfb_free_screen(void *v, void *cookie) 551 { 552 struct ssdfb_softc *sc = v; 553 554 if (sc->sc_is_console) 555 panic("ssdfb_free_screen: is console"); 556 557 sc->sc_nscreens--; 558 } 559 560 static int 561 ssdfb_show_screen(void *v, void *cookie, int waitok, 562 void (*cb) (void *, int, int), void *cb_arg) 563 { 564 return 0; 565 } 566 567 static void 568 ssdfb_putchar(void *cookie, int row, int col, u_int c, long attr) 569 { 570 struct rasops_info *ri = (struct rasops_info *)cookie; 571 struct ssdfb_softc *sc = ri->ri_hw; 572 573 sc->sc_orig_riops.putchar(cookie, row, col, c, attr); 574 ssdfb_damage(sc); 575 } 576 577 static void 578 ssdfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 579 { 580 struct rasops_info *ri = (struct rasops_info *)cookie; 581 struct ssdfb_softc *sc = ri->ri_hw; 582 583 sc->sc_orig_riops.copycols(cookie, row, srccol, dstcol, ncols); 584 ssdfb_damage(sc); 585 } 586 587 static void 588 ssdfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 589 { 590 struct rasops_info *ri = (struct rasops_info *)cookie; 591 struct ssdfb_softc *sc = ri->ri_hw; 592 593 sc->sc_orig_riops.erasecols(cookie, row, startcol, ncols, fillattr); 594 ssdfb_damage(sc); 595 } 596 597 static void 598 ssdfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 599 { 600 struct rasops_info *ri = (struct rasops_info *)cookie; 601 struct ssdfb_softc *sc = ri->ri_hw; 602 603 sc->sc_orig_riops.copyrows(cookie, srcrow, dstrow, nrows); 604 ssdfb_damage(sc); 605 } 606 607 static void 608 ssdfb_eraserows(void *cookie, int row, int nrows, long fillattr) 609 { 610 struct rasops_info *ri = (struct rasops_info *)cookie; 611 struct ssdfb_softc *sc = ri->ri_hw; 612 613 sc->sc_orig_riops.eraserows(cookie, row, nrows, fillattr); 614 ssdfb_damage(sc); 615 } 616 617 static void 618 ssdfb_cursor(void *cookie, int on, int row, int col) 619 { 620 struct rasops_info *ri = (struct rasops_info *)cookie; 621 struct ssdfb_softc *sc = ri->ri_hw; 622 623 sc->sc_orig_riops.cursor(cookie, on, row, col); 624 ssdfb_damage(sc); 625 } 626 627 static int 628 ssdfb_init_ssd1306(struct ssdfb_softc *sc) 629 { 630 int error; 631 uint8_t cmd[2]; 632 bool usepoll = true; 633 634 /* 635 * Enter sleep. 636 */ 637 SSDFB_CMD1(SSDFB_CMD_SET_DISPLAY_OFF); 638 if (error) 639 return error; 640 SSDFB_CMD1(SSDFB_CMD_DEACTIVATE_SCROLL); 641 if (error) 642 return error; 643 SSDFB_CMD1(SSDFB_CMD_ENTIRE_DISPLAY_OFF); 644 if (error) 645 return error; 646 647 /* 648 * Configure physical display panel layout. 649 */ 650 SSDFB_CMD2(SSDFB_CMD_SET_MULTIPLEX_RATIO, sc->sc_p->p_multiplex_ratio); 651 if (error) 652 return error; 653 SSDFB_CMD2(SSDFB_CMD_SET_DISPLAY_OFFSET, 0); 654 if (error) 655 return error; 656 SSDFB_CMD1(SSDFB_CMD_SET_DISPLAY_START_LINE_BASE + 0x00); 657 if (error) 658 return error; 659 SSDFB_CMD2(SSDFB_CMD_SET_COM_PINS_HARDWARE_CFG, sc->sc_p->p_compin_cfg); 660 if (error) 661 return error; 662 if (sc->sc_upsidedown) { 663 SSDFB_CMD1(SSDFB_CMD_SET_SEGMENT_REMAP_REVERSE); 664 if (error) 665 return error; 666 SSDFB_CMD1(SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_REMAP); 667 if (error) 668 return error; 669 } else { 670 SSDFB_CMD1(SSDFB_CMD_SET_SEGMENT_REMAP_NORMAL); 671 if (error) 672 return error; 673 SSDFB_CMD1(SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_NORMAL); 674 if (error) 675 return error; 676 } 677 SSDFB_CMD1(SSDFB_CMD_SET_NORMAL_DISPLAY + (uint8_t)sc->sc_inverse); 678 if (error) 679 return error; 680 681 /* 682 * Configure timing characteristics. 683 */ 684 SSDFB_CMD2(SSDFB_CMD_SET_DISPLAY_CLOCK_RATIO, 685 __SHIFTIN(sc->sc_p->p_fosc, SSDFB_DISPLAY_CLOCK_OSCILLATOR_MASK) | 686 __SHIFTIN(sc->sc_p->p_fosc_div, SSDFB_DISPLAY_CLOCK_DIVIDER_MASK)); 687 if (error) 688 return error; 689 SSDFB_CMD2(SSDFB_CMD_SET_CONTRAST_CONTROL, sc->sc_contrast); 690 if (error) 691 return error; 692 SSDFB_CMD2(SSDFB_CMD_SET_PRECHARGE_PERIOD, 693 __SHIFTIN(sc->sc_p->p_precharge, SSDFB_PRECHARGE_MASK) | 694 __SHIFTIN(sc->sc_p->p_discharge, SSDFB_DISCHARGE_MASK)); 695 if (error) 696 return error; 697 SSDFB_CMD2(SSDFB_CMD_SET_VCOMH_DESELECT_LEVEL, 698 sc->sc_p->p_vcomh_deselect_level); 699 if (error) 700 return error; 701 702 /* 703 * Start charge pumps. 704 */ 705 if (sc->sc_p->p_controller_id == SSDFB_CONTROLLER_SH1106) { 706 SSDFB_CMD1(SH1106_CMD_SET_CHARGE_PUMP_7V4); 707 if (error) 708 return error; 709 SSDFB_CMD2(SH1106_CMD_SET_DC_DC, SH1106_DC_DC_ON); 710 if (error) 711 return error; 712 } else { 713 SSDFB_CMD2(SSD1306_CMD_SET_CHARGE_PUMP, 714 SSD1306_CHARGE_PUMP_ENABLE); 715 if (error) 716 return error; 717 } 718 719 ssdfb_clear_screen(sc); 720 error = sc->sc_p->p_sync(sc, usepoll); 721 if (error) 722 return error; 723 error = ssdfb_set_display_on(sc, true, usepoll); 724 725 return error; 726 } 727 728 static int 729 ssdfb_init_ssd1322(struct ssdfb_softc *sc) 730 { 731 int error; 732 uint8_t cmd[3]; 733 bool usepoll = true; 734 uint8_t remap; 735 uint8_t dualcom; 736 737 /* 738 * Enter sleep. 739 */ 740 SSDFB_CMD2(SSD1322_CMD_SET_COMMAND_LOCK, SSD1322_COMMAND_UNLOCK_MAGIC); 741 if (error) 742 return error; 743 SSDFB_CMD1(SSD1322_CMD_SET_SLEEP_MODE_ON); 744 if (error) 745 return error; 746 747 /* 748 * Start charge pumps. 749 */ 750 SSDFB_CMD2(SSD1322_CMD_FUNCTION_SELECTION, 751 SSD1322_FUNCTION_SELECTION_INTERNAL_VDD); 752 if (error) 753 return error; 754 SSDFB_CMD2(SSD1322_CMD_SET_VCOMH, sc->sc_p->p_vcomh_deselect_level); 755 if (error) 756 return error; 757 SSDFB_CMD2(SSD1322_CMD_SET_PRE_CHARGE_VOLTAGE_LEVEL, 758 SSD1322_DEFAULT_PRE_CHARGE_VOLTAGE_LEVEL); 759 if (error) 760 return error; 761 SSDFB_CMD2(SSD1322_CMD_SET_GPIO, 762 SSD1322_GPIO0_DISABLED | SSD1322_GPIO1_DISABLED); 763 if (error) 764 return error; 765 766 /* 767 * Configure timing characteristics. 768 */ 769 SSDFB_CMD2(SSD1322_CMD_SET_FRONT_CLOCK_DIVIDER, 770 __SHIFTIN(sc->sc_p->p_fosc, SSD1322_FREQUENCY_MASK) | 771 __SHIFTIN(sc->sc_p->p_fosc_div, SSD1322_DIVIDER_MASK)); 772 if (error) 773 return error; 774 SSDFB_CMD2(SSD1322_CMD_SET_PHASE_LENGTH, 775 __SHIFTIN(SSD1322_DEFAULT_PHASE_2, 776 SSD1322_PHASE_LENGTH_PHASE_2_MASK) | 777 __SHIFTIN(SSD1322_DEFAULT_PHASE_1, 778 SSD1322_PHASE_LENGTH_PHASE_1_MASK)); 779 if (error) 780 return error; 781 SSDFB_CMD2(SSD1322_CMD_SET_SECOND_PRECHARGE_PERIOD, 782 SSD1322_DEFAULT_SECOND_PRECHARGE); 783 if (error) 784 return error; 785 786 /* 787 * Configure physical display panel layout. 788 */ 789 SSDFB_CMD2(SSD1322_CMD_SET_MUX_RATIO, sc->sc_p->p_multiplex_ratio); 790 if (error) 791 return error; 792 if (sc->sc_upsidedown) 793 remap = 0x10; 794 else 795 remap = 0x2; 796 dualcom = 0x1; 797 if (sc->sc_p->p_multiplex_ratio <= 63) 798 dualcom |= 0x10; 799 SSDFB_CMD3(SSD1322_CMD_SET_REMAP_AND_DUAL_COM_LINE_MODE, remap, dualcom); 800 if (error) 801 return error; 802 803 /* 804 * Contrast settings. 805 */ 806 SSDFB_CMD1(SSD1322_CMD_SET_DEFAULT_GRAY_SCALE_TABLE); 807 if (error) 808 return error; 809 SSDFB_CMD3(SSD1322_CMD_DISPLAY_ENHANCEMENT_A, 810 SSD1322_DISPLAY_ENHANCEMENT_A_MAGIC1, 811 SSD1322_DISPLAY_ENHANCEMENT_A_MAGIC2); 812 if (error) 813 return error; 814 SSDFB_CMD3(SSD1322_CMD_DISPLAY_ENHANCEMENT_B, 815 SSD1322_DISPLAY_ENHANCEMENT_B_MAGIC1, 816 SSD1322_DISPLAY_ENHANCEMENT_B_MAGIC2); 817 if (error) 818 return error; 819 SSDFB_CMD2(SSD1322_CMD_SET_CONTRAST_CURRENT, 820 sc->sc_contrast); 821 if (error) 822 return error; 823 SSDFB_CMD2(SSD1322_CMD_MASTER_CONTRAST_CURRENT_CONTROL, 824 SSD1322_DEFAULT_MASTER_CONTRAST_CURRENT_CONTROL); 825 if (error) 826 return error; 827 828 /* 829 * Reset display engine state. 830 */ 831 SSDFB_CMD2(SSD1322_CMD_SET_DISPLAY_OFFSET, 0x00); 832 if (error) 833 return error; 834 SSDFB_CMD2(SSD1322_CMD_SET_DISPLAY_START_LINE, 0x00); 835 if (error) 836 return error; 837 SSDFB_CMD1(SSD1322_CMD_NORMAL_DISPLAY + (uint8_t)sc->sc_inverse); 838 if (error) 839 return error; 840 SSDFB_CMD1(SSD1322_CMD_EXIT_PARTIAL_DISPLAY); 841 if (error) 842 return error; 843 844 ssdfb_clear_screen(sc); 845 error = ssdfb_sync(sc, usepoll); 846 if (error) 847 return error; 848 849 error = ssdfb_set_display_on(sc, true, usepoll); 850 851 return error; 852 } 853 854 static int 855 ssdfb_set_contrast(struct ssdfb_softc *sc, uint8_t value, bool usepoll) 856 { 857 uint8_t cmd[2]; 858 859 switch (sc->sc_p->p_controller_id) { 860 case SSDFB_CONTROLLER_SSD1322: 861 cmd[0] = SSD1322_CMD_SET_CONTRAST_CURRENT; 862 break; 863 default: 864 cmd[0] = SSDFB_CMD_SET_CONTRAST_CONTROL; 865 } 866 cmd[1] = sc->sc_contrast = value; 867 868 return sc->sc_cmd(sc->sc_cookie, cmd, sizeof(cmd), usepoll); 869 } 870 871 static int 872 ssdfb_set_display_on(struct ssdfb_softc *sc, bool value, bool usepoll) 873 { 874 uint8_t cmd[1]; 875 int error; 876 sc->sc_display_on = value; 877 878 SSDFB_CMD1(value ? SSDFB_CMD_SET_DISPLAY_ON : SSDFB_CMD_SET_DISPLAY_OFF); 879 880 return error; 881 } 882 883 static int 884 ssdfb_set_mode(struct ssdfb_softc *sc, u_int mode) 885 { 886 switch (mode) { 887 case WSDISPLAYIO_MODE_EMUL: 888 case WSDISPLAYIO_MODE_DUMBFB: 889 break; 890 default: 891 return EINVAL; 892 } 893 if (mode == sc->sc_mode) 894 return 0; 895 mutex_enter(&sc->sc_cond_mtx); 896 sc->sc_mode = mode; 897 cv_broadcast(&sc->sc_cond); 898 mutex_exit(&sc->sc_cond_mtx); 899 ssdfb_clear_screen(sc); 900 ssdfb_damage(sc); 901 902 return 0; 903 } 904 905 static void 906 ssdfb_damage(struct ssdfb_softc *sc) 907 { 908 int s; 909 910 if (sc->sc_usepoll) { 911 (void) ssdfb_sync(sc, true); 912 } else { 913 /* 914 * kernel code isn't permitted to call us via kprintf at 915 * splhigh. In case misbehaving code calls us anyway we can't 916 * safely take the mutex so we skip the damage notification. 917 */ 918 if (sc->sc_is_console) { 919 s = splhigh(); 920 splx(s); 921 if (s == IPL_HIGH) 922 return; 923 } 924 mutex_enter(&sc->sc_cond_mtx); 925 sc->sc_modified = true; 926 cv_broadcast(&sc->sc_cond); 927 mutex_exit(&sc->sc_cond_mtx); 928 } 929 } 930 931 static void 932 ssdfb_udv_attach(struct ssdfb_softc *sc) 933 { 934 extern const struct cdevsw wsdisplay_cdevsw; 935 dev_t dev; 936 #define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen)) 937 dev = makedev(cdevsw_lookup_major(&wsdisplay_cdevsw), 938 WSDISPLAYMINOR(device_unit(sc->sc_wsdisplay), 0)); 939 sc->sc_uobj = udv_attach(dev, VM_PROT_READ|VM_PROT_WRITE, 0, 940 sc->sc_ri_bits_len); 941 } 942 943 static bool 944 ssdfb_is_modified(struct ssdfb_softc *sc) 945 { 946 vaddr_t va, va_end; 947 948 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) 949 return sc->sc_modified; 950 951 if (sc->sc_uobj == NULL) 952 return false; 953 954 va = (vaddr_t)sc->sc_ri.ri_bits; 955 va_end = va + sc->sc_ri_bits_len; 956 while (va < va_end) { 957 if (pmap_is_modified(uvm_pageratop(va))) 958 return true; 959 va += PAGE_SIZE; 960 } 961 962 return false; 963 } 964 965 static bool 966 ssdfb_clear_modify(struct ssdfb_softc *sc) 967 { 968 vaddr_t va, va_end; 969 bool ret; 970 971 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) { 972 mutex_enter(&sc->sc_cond_mtx); 973 ret = sc->sc_modified; 974 sc->sc_modified = false; 975 mutex_exit(&sc->sc_cond_mtx); 976 return ret; 977 } 978 979 if (sc->sc_uobj == NULL) 980 return false; 981 982 va = (vaddr_t)sc->sc_ri.ri_bits; 983 va_end = va + sc->sc_ri_bits_len; 984 ret = false; 985 while (va < va_end) { 986 if (pmap_clear_modify(uvm_pageratop(va))) 987 ret = true; 988 va += PAGE_SIZE; 989 } 990 991 return ret; 992 } 993 994 static void 995 ssdfb_thread(void *arg) 996 { 997 struct ssdfb_softc *sc = (struct ssdfb_softc *)arg; 998 int error; 999 1000 mutex_enter(&sc->sc_cond_mtx); 1001 1002 if (sc->sc_usepoll) 1003 ssdfb_set_usepoll(sc, false); 1004 1005 while(!sc->sc_detaching) { 1006 if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB && 1007 sc->sc_uobj == NULL) { 1008 mutex_exit(&sc->sc_cond_mtx); 1009 ssdfb_udv_attach(sc); 1010 mutex_enter(&sc->sc_cond_mtx); 1011 } 1012 if (!ssdfb_is_modified(sc)) { 1013 if (cv_timedwait(&sc->sc_cond, &sc->sc_cond_mtx, 1014 sc->sc_mode == WSDISPLAYIO_MODE_EMUL 1015 ? 0 : sc->sc_backoff) == EWOULDBLOCK 1016 && sc->sc_backoff < mstohz(200)) { 1017 sc->sc_backoff <<= 1; 1018 } 1019 continue; 1020 } 1021 sc->sc_backoff = 1; 1022 mutex_exit(&sc->sc_cond_mtx); 1023 (void) ssdfb_clear_modify(sc); 1024 if (!sc->sc_usepoll) { 1025 error = ssdfb_sync(sc, false); 1026 if (error) 1027 device_printf(sc->sc_dev, 1028 "ssdfb_sync: error %d\n", 1029 error); 1030 } 1031 mutex_enter(&sc->sc_cond_mtx); 1032 } 1033 1034 mutex_exit(&sc->sc_cond_mtx); 1035 kthread_exit(0); 1036 } 1037 1038 static void 1039 ssdfb_set_usepoll(struct ssdfb_softc *sc, bool enable) 1040 { 1041 sc->sc_usepoll = enable; 1042 } 1043 1044 static int 1045 ssdfb_sync(struct ssdfb_softc *sc, bool usepoll) 1046 { 1047 return sc->sc_p->p_sync(sc, usepoll); 1048 } 1049 1050 static int 1051 ssdfb_sync_ssd1306(struct ssdfb_softc *sc, bool usepoll) 1052 { 1053 struct rasops_info *ri = &sc->sc_ri; 1054 int block_size = 8; 1055 int ri_block_stride = ri->ri_stride * block_size; 1056 int height_in_blocks = sc->sc_p->p_height / block_size; 1057 int width_in_blocks = sc->sc_p->p_width / block_size; 1058 int ri_block_step = block_size * ri->ri_depth / 8; 1059 int x, y; 1060 union ssdfb_block *blockp; 1061 uint64_t raw_block; 1062 uint8_t *src; 1063 int x1, x2, y1, y2; 1064 1065 /* 1066 * Transfer rasops bitmap into gddram shadow buffer while keeping track 1067 * of the bounding box of the dirty region we scribbled over. 1068 */ 1069 x1 = width_in_blocks; 1070 x2 = -1; 1071 y1 = height_in_blocks; 1072 y2 = -1; 1073 for (y = 0; y < height_in_blocks; y++) { 1074 src = &ri->ri_bits[y * ri_block_stride]; 1075 blockp = &sc->sc_gddram[y * width_in_blocks]; 1076 for (x = 0; x < width_in_blocks; x++) { 1077 raw_block = ssdfb_transpose_block(src, ri->ri_stride); 1078 if (raw_block != blockp->raw) { 1079 blockp->raw = raw_block; 1080 if (x1 > x) 1081 x1 = x; 1082 if (x2 < x) 1083 x2 = x; 1084 if (y1 > y) 1085 y1 = y; 1086 if (y2 < y) 1087 y2 = y; 1088 } 1089 src += ri_block_step; 1090 blockp++; 1091 } 1092 } 1093 if (x2 != -1) 1094 return sc->sc_transfer_rect(sc->sc_cookie, 1095 x1 * block_size + sc->sc_p->p_panel_shift, 1096 (x2 + 1) * block_size - 1 + sc->sc_p->p_panel_shift, 1097 y1, 1098 y2, 1099 &sc->sc_gddram[y1 * width_in_blocks + x1].col[0], 1100 sc->sc_p->p_width, 1101 usepoll); 1102 1103 return 0; 1104 } 1105 1106 static int 1107 ssdfb_sync_ssd1322(struct ssdfb_softc *sc, bool usepoll) 1108 { 1109 struct rasops_info *ri = &sc->sc_ri; 1110 int block_size_w = 4; 1111 int width = sc->sc_p->p_width; 1112 int height = sc->sc_p->p_height; 1113 int width_in_blocks = width / block_size_w; 1114 int x, y; 1115 uint16_t *blockp; 1116 uint16_t raw_block; 1117 uint16_t *src; 1118 uint32_t *src32; 1119 int x1, x2, y1, y2; 1120 1121 /* 1122 * Transfer rasops bitmap into gddram shadow buffer while keeping track 1123 * of the bounding box of the dirty region we scribbled over. 1124 */ 1125 x1 = sc->sc_p->p_width; 1126 x2 = -1; 1127 y1 = sc->sc_p->p_height; 1128 y2 = -1; 1129 blockp = (uint16_t*)sc->sc_gddram; 1130 for (y = 0; y < height; y++) { 1131 src = (uint16_t*)&ri->ri_bits[y * ri->ri_stride]; 1132 src32 = (uint32_t*)src; 1133 for (x = 0; x < width_in_blocks; x++) { 1134 #if _BYTE_ORDER == _LITTLE_ENDIAN 1135 # ifdef SSDFB_USE_NATIVE_DEPTH 1136 raw_block = 1137 ((*src << 12) & 0xf000) | 1138 ((*src << 4) & 0x0f00) | 1139 ((*src >> 4) & 0x00f0) | 1140 ((*src >> 12) & 0x000f); 1141 src++; 1142 # else 1143 raw_block = 1144 ((*src32 << 8) & 0x0f00) | 1145 ((*src32 << 4) & 0xf000) | 1146 ((*src32 >> 16) & 0x000f) | 1147 ((*src32 >> 20) & 0x00f0); 1148 # endif 1149 src32++; 1150 #else 1151 # error please add big endian host support here 1152 #endif 1153 if (raw_block != *blockp) { 1154 *blockp = raw_block; 1155 if (x1 > x) 1156 x1 = x; 1157 if (x2 < x) 1158 x2 = x; 1159 if (y1 > y) 1160 y1 = y; 1161 if (y2 < y) 1162 y2 = y; 1163 } 1164 blockp++; 1165 } 1166 } 1167 1168 blockp = (uint16_t*)sc->sc_gddram; 1169 if (x2 != -1) 1170 return sc->sc_transfer_rect(sc->sc_cookie, 1171 x1 + sc->sc_p->p_panel_shift, 1172 x2 + sc->sc_p->p_panel_shift, 1173 y1, 1174 y2, 1175 (uint8_t*)&blockp[y1 * width_in_blocks + x1], 1176 width * sc->sc_p->p_bits_per_pixel / 8, 1177 usepoll); 1178 return 0; 1179 } 1180 1181 static uint64_t 1182 ssdfb_transpose_block(uint8_t *src, size_t src_stride) 1183 { 1184 uint64_t x = 0; 1185 #ifdef SSDFB_USE_NATIVE_DEPTH 1186 uint64_t t; 1187 int i; 1188 1189 /* 1190 * collect the 8x8 block. 1191 */ 1192 for (i = 0; i < 8; i++) { 1193 x >>= 8; 1194 x |= (uint64_t)src[i * src_stride] << 56; 1195 } 1196 1197 /* 1198 * Transpose it into gddram layout. 1199 * Post-transpose bswap is the same as pre-transpose bit order reversal. 1200 * We do this to match rasops1 bit order. 1201 */ 1202 t = (x ^ (x >> 28)) & 0x00000000F0F0F0F0ULL; 1203 x = x ^ t ^ (t << 28); 1204 t = (x ^ (x >> 14)) & 0x0000CCCC0000CCCCULL; 1205 x = x ^ t ^ (t << 14); 1206 t = (x ^ (x >> 7)) & 0x00AA00AA00AA00AAULL; 1207 x = x ^ t ^ (t << 7); 1208 x = bswap64(x); 1209 #else 1210 int m, n; 1211 1212 for (m = 0; m < 8; m++) { 1213 for (n = 0; n < 8; n++) { 1214 x >>= 1; 1215 x |= src[n * src_stride + m] ? (1ULL << 63) : 0; 1216 } 1217 } 1218 #endif 1219 return htole64(x); 1220 } 1221 1222 static const struct ssdfb_product * 1223 ssdfb_lookup_product(ssdfb_product_id_t id) 1224 { 1225 int i; 1226 1227 for (i = 0; i < __arraycount(ssdfb_products); i++) { 1228 if (ssdfb_products[i].p_product_id == id) 1229 return &ssdfb_products[i]; 1230 } 1231 1232 return NULL; 1233 } 1234 1235 static int 1236 ssdfb_pick_font(int *cookiep, struct wsdisplay_font **fontp) 1237 { 1238 int error; 1239 int c; 1240 struct wsdisplay_font *f; 1241 int i; 1242 uint8_t d[4][2] = {{5, 8}, {8, 8}, {8, 10} ,{8, 16}}; 1243 1244 /* 1245 * Try to find fonts in order of increasing size. 1246 */ 1247 wsfont_init(); 1248 for(i = 0; i < __arraycount(d); i++) { 1249 c = wsfont_find(NULL, d[i][0], d[i][1], 0, 1250 WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R, 1251 WSFONT_FIND_BITMAP); 1252 if (c > 0) 1253 break; 1254 } 1255 if (c <= 0) 1256 return ENOENT; 1257 error = wsfont_lock(c, &f); 1258 if (error) 1259 return error; 1260 *cookiep = c; 1261 *fontp = f; 1262 1263 return 0; 1264 } 1265 1266 static void 1267 ssdfb_clear_screen(struct ssdfb_softc *sc) 1268 { 1269 struct rasops_info *ri = &sc->sc_ri; 1270 1271 memset(sc->sc_gddram, 0xff, sc->sc_gddram_len); 1272 memset(ri->ri_bits, 0, sc->sc_ri_bits_len); 1273 } 1274 1275 #if defined(DDB) 1276 static void 1277 ssdfb_ddb_trap_callback(int enable) 1278 { 1279 extern struct cfdriver ssdfb_cd; 1280 struct ssdfb_softc *sc; 1281 int i; 1282 1283 for (i = 0; i < ssdfb_cd.cd_ndevs; i++) { 1284 sc = device_lookup_private(&ssdfb_cd, i); 1285 if (sc != NULL && sc->sc_is_console) { 1286 ssdfb_set_usepoll(sc, (bool)enable); 1287 } 1288 } 1289 } 1290 #endif 1291