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