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