1*8e57c9bfStnn /* $NetBSD: ssdfb.c,v 1.8 2019/10/22 22:03:27 tnn Exp $ */ 2e690818eStnn 3e690818eStnn /* 4e690818eStnn * Copyright (c) 2019 The NetBSD Foundation, Inc. 5e690818eStnn * All rights reserved. 6e690818eStnn * 7e690818eStnn * This code is derived from software contributed to The NetBSD Foundation 8e690818eStnn * by Tobias Nygren. 9e690818eStnn * 10e690818eStnn * Redistribution and use in source and binary forms, with or without 11e690818eStnn * modification, are permitted provided that the following conditions 12e690818eStnn * are met: 13e690818eStnn * 1. Redistributions of source code must retain the above copyright 14e690818eStnn * notice, this list of conditions and the following disclaimer. 15e690818eStnn * 2. Redistributions in binary form must reproduce the above copyright 16e690818eStnn * notice, this list of conditions and the following disclaimer in the 17e690818eStnn * documentation and/or other materials provided with the distribution. 18e690818eStnn * 19e690818eStnn * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20e690818eStnn * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21e690818eStnn * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22e690818eStnn * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23e690818eStnn * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24e690818eStnn * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25e690818eStnn * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26e690818eStnn * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27e690818eStnn * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28e690818eStnn * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29e690818eStnn * POSSIBILITY OF SUCH DAMAGE. 30e690818eStnn */ 31e690818eStnn 32e690818eStnn #include <sys/cdefs.h> 33*8e57c9bfStnn __KERNEL_RCSID(0, "$NetBSD: ssdfb.c,v 1.8 2019/10/22 22:03:27 tnn Exp $"); 34e690818eStnn 35e690818eStnn #include "opt_ddb.h" 36e690818eStnn 37e690818eStnn #include <sys/param.h> 38e690818eStnn #include <sys/kernel.h> 3933ea73e5Stnn #include <sys/conf.h> 406f6ddff5Stnn #include <uvm/uvm.h> 41e690818eStnn #include <uvm/uvm_page.h> 4233ea73e5Stnn #include <uvm/uvm_device.h> 43e690818eStnn #include <sys/condvar.h> 44e690818eStnn #include <sys/kmem.h> 45e690818eStnn #include <sys/kthread.h> 46e690818eStnn #include <dev/wscons/wsdisplayvar.h> 47e690818eStnn #include <dev/rasops/rasops.h> 48e690818eStnn #include <dev/ic/ssdfbvar.h> 49e690818eStnn 50e690818eStnn #if defined(DDB) 51e690818eStnn #include <machine/db_machdep.h> 52e690818eStnn #include <ddb/db_extern.h> 53e690818eStnn #endif 54e690818eStnn 55e690818eStnn /* userland interface */ 56e690818eStnn static int ssdfb_ioctl(void *, void *, u_long, void *, int, struct lwp *); 57e690818eStnn static paddr_t ssdfb_mmap(void *, void *, off_t, int); 58e690818eStnn 59e690818eStnn /* wscons screen management */ 60e690818eStnn static int ssdfb_alloc_screen(void *, const struct wsscreen_descr *, 61e690818eStnn void **, int *, int *, long *); 62e690818eStnn static void ssdfb_free_screen(void *, void *); 63e690818eStnn static int ssdfb_show_screen(void *, void *, int, 64e690818eStnn void (*cb) (void *, int, int), void *); 65e690818eStnn 66e690818eStnn /* rasops hooks */ 67e690818eStnn static void ssdfb_putchar(void *, int, int, u_int, long); 68e690818eStnn static void ssdfb_copycols(void *, int, int, int, int); 69e690818eStnn static void ssdfb_erasecols(void *, int, int, int, long); 70e690818eStnn static void ssdfb_copyrows(void *, int, int, int); 71e690818eStnn static void ssdfb_eraserows(void *, int, int, long); 72e690818eStnn static void ssdfb_cursor(void *, int, int, int); 73e690818eStnn 74e690818eStnn /* hardware interface */ 75*8e57c9bfStnn static int ssdfb_init_ssd1306(struct ssdfb_softc *); 76e690818eStnn static int ssdfb_set_contrast(struct ssdfb_softc *, uint8_t, bool); 77e690818eStnn static int ssdfb_set_display_on(struct ssdfb_softc *, bool, bool); 78e690818eStnn static int ssdfb_set_mode(struct ssdfb_softc *, u_int); 79e690818eStnn 80e690818eStnn /* frame buffer damage tracking and synchronization */ 8133ea73e5Stnn static void ssdfb_udv_attach(struct ssdfb_softc *sc); 82e690818eStnn static bool ssdfb_is_modified(struct ssdfb_softc *sc); 83e690818eStnn static bool ssdfb_clear_modify(struct ssdfb_softc *sc); 84e690818eStnn static void ssdfb_damage(struct ssdfb_softc *); 85e690818eStnn static void ssdfb_thread(void *); 86e690818eStnn static void ssdfb_set_usepoll(struct ssdfb_softc *, bool); 87e690818eStnn static int ssdfb_sync(struct ssdfb_softc *, bool); 88e690818eStnn static uint64_t ssdfb_transpose_block_1bpp(uint8_t *, size_t); 89e690818eStnn static uint64_t ssdfb_transpose_block_8bpp(uint8_t *, size_t); 90e690818eStnn 91e690818eStnn /* misc helpers */ 92e690818eStnn static const struct ssdfb_product * 93e690818eStnn ssdfb_lookup_product(ssdfb_product_id_t); 94e690818eStnn static int ssdfb_pick_font(int *, struct wsdisplay_font **); 95e690818eStnn static void ssdfb_clear_screen(struct ssdfb_softc *); 96e690818eStnn #if defined(DDB) 97e690818eStnn static void ssdfb_ddb_trap_callback(int); 98e690818eStnn #endif 99e690818eStnn 100e690818eStnn static const char *ssdfb_controller_names[] = { 101e690818eStnn [SSDFB_CONTROLLER_UNKNOWN] = "unknown", 102e690818eStnn [SSDFB_CONTROLLER_SSD1306] = "Solomon Systech SSD1306", 103*8e57c9bfStnn [SSDFB_CONTROLLER_SH1106] = "Sino Wealth SH1106", 104*8e57c9bfStnn [SSDFB_CONTROLLER_SSD1322] = "Solomon Systech SSD1322" 105e690818eStnn }; 106e690818eStnn 107e690818eStnn /* 108e690818eStnn * Display module assemblies supported by this driver. 109e690818eStnn */ 110e690818eStnn static const struct ssdfb_product ssdfb_products[] = { 111e690818eStnn { 112e690818eStnn .p_product_id = SSDFB_PRODUCT_SSD1306_GENERIC, 113e690818eStnn .p_controller_id = SSDFB_CONTROLLER_SSD1306, 114e690818eStnn .p_name = "generic", 115e690818eStnn .p_width = 128, 116e690818eStnn .p_height = 64, 117e690818eStnn .p_panel_shift = 0, 118e690818eStnn .p_fosc = 0x8, 119e690818eStnn .p_fosc_div = 0, 120e690818eStnn .p_precharge = 0x1, 121e690818eStnn .p_discharge = 0xf, 122e690818eStnn .p_compin_cfg = SSDFB_COM_PINS_A1_MASK 123e690818eStnn | SSDFB_COM_PINS_ALTERNATIVE_MASK, 124e690818eStnn .p_vcomh_deselect_level = SSD1306_VCOMH_DESELECT_LEVEL_0_77_VCC, 125e690818eStnn .p_default_contrast = 0x7f, 126e690818eStnn .p_multiplex_ratio = 0x3f, 127e690818eStnn .p_chargepump_cmd = SSD1306_CMD_SET_CHARGE_PUMP, 128*8e57c9bfStnn .p_chargepump_arg = SSD1306_CHARGE_PUMP_ENABLE, 129*8e57c9bfStnn .p_init = ssdfb_init_ssd1306 130e690818eStnn }, 131e690818eStnn { 132e690818eStnn .p_product_id = SSDFB_PRODUCT_SH1106_GENERIC, 133e690818eStnn .p_controller_id = SSDFB_CONTROLLER_SH1106, 134e690818eStnn .p_name = "generic", 135e690818eStnn .p_width = 128, 136e690818eStnn .p_height = 64, 137e690818eStnn .p_panel_shift = 2, 138e690818eStnn .p_fosc = 0x5, 139e690818eStnn .p_fosc_div = 0, 140e690818eStnn .p_precharge = 0x2, 141e690818eStnn .p_discharge = 0x2, 142e690818eStnn .p_compin_cfg = SSDFB_COM_PINS_A1_MASK 143e690818eStnn | SSDFB_COM_PINS_ALTERNATIVE_MASK, 144e690818eStnn .p_vcomh_deselect_level = SH1106_VCOMH_DESELECT_LEVEL_DEFAULT, 145e690818eStnn .p_default_contrast = 0x80, 146e690818eStnn .p_multiplex_ratio = 0x3f, 147e690818eStnn .p_chargepump_cmd = SH1106_CMD_SET_CHARGE_PUMP_7V4, 148*8e57c9bfStnn .p_chargepump_arg = SSDFB_CMD_NOP, 149*8e57c9bfStnn .p_init = ssdfb_init_ssd1306 150e690818eStnn }, 151e690818eStnn { 152e690818eStnn .p_product_id = SSDFB_PRODUCT_ADAFRUIT_938, 153e690818eStnn .p_controller_id = SSDFB_CONTROLLER_SSD1306, 154e690818eStnn .p_name = "Adafruit Industries, LLC product 938", 155e690818eStnn .p_width = 128, 156e690818eStnn .p_height = 64, 157e690818eStnn .p_panel_shift = 0, 158e690818eStnn .p_fosc = 0x8, 159e690818eStnn .p_fosc_div = 0, 160e690818eStnn .p_precharge = 0x1, 161e690818eStnn .p_discharge = 0xf, 162e690818eStnn .p_compin_cfg = 0x12, 163e690818eStnn .p_vcomh_deselect_level = 0x40, 164e690818eStnn .p_default_contrast = 0x8f, 165e690818eStnn .p_multiplex_ratio = 0x3f, 166e690818eStnn .p_chargepump_cmd = SSD1306_CMD_SET_CHARGE_PUMP, 167*8e57c9bfStnn .p_chargepump_arg = SSD1306_CHARGE_PUMP_ENABLE, 168*8e57c9bfStnn .p_init = ssdfb_init_ssd1306 169e690818eStnn }, 170e690818eStnn { 171e690818eStnn .p_product_id = SSDFB_PRODUCT_ADAFRUIT_931, 172e690818eStnn .p_controller_id = SSDFB_CONTROLLER_SSD1306, 173e690818eStnn .p_name = "Adafruit Industries, LLC product 931", 174e690818eStnn .p_width = 128, 175e690818eStnn .p_height = 32, 176e690818eStnn .p_panel_shift = 0, 177e690818eStnn .p_fosc = 0x8, 178e690818eStnn .p_fosc_div = 0, 179e690818eStnn .p_precharge = 0x1, 180e690818eStnn .p_discharge = 0xf, 181e690818eStnn .p_compin_cfg = 0x2, 182e690818eStnn .p_vcomh_deselect_level = 0x40, 183e690818eStnn .p_default_contrast = 0x8f, 184e690818eStnn .p_multiplex_ratio = 0x1f, 185e690818eStnn .p_chargepump_cmd = SSD1306_CMD_SET_CHARGE_PUMP, 186*8e57c9bfStnn .p_chargepump_arg = SSD1306_CHARGE_PUMP_ENABLE, 187*8e57c9bfStnn .p_init = ssdfb_init_ssd1306 188e690818eStnn } 189e690818eStnn }; 190e690818eStnn 191e690818eStnn static const struct wsdisplay_accessops ssdfb_accessops = { 192e690818eStnn .ioctl = ssdfb_ioctl, 193e690818eStnn .mmap = ssdfb_mmap, 194e690818eStnn .alloc_screen = ssdfb_alloc_screen, 195e690818eStnn .free_screen = ssdfb_free_screen, 196e690818eStnn .show_screen = ssdfb_show_screen 197e690818eStnn }; 198e690818eStnn 199e690818eStnn #define SSDFB_CMD1(c) do { cmd[0] = (c); error = sc->sc_cmd(sc->sc_cookie, cmd, 1, usepoll); } while(0) 200e690818eStnn #define SSDFB_CMD2(c, a) do { cmd[0] = (c); cmd[1] = (a); error = sc->sc_cmd(sc->sc_cookie, cmd, 2, usepoll); } while(0) 201e690818eStnn 202e690818eStnn void 203e690818eStnn ssdfb_attach(struct ssdfb_softc *sc, int flags) 204e690818eStnn { 205e690818eStnn struct wsemuldisplaydev_attach_args aa; 206e690818eStnn struct rasops_info *ri = &sc->sc_ri; 207e690818eStnn int error = 0; 208e690818eStnn long defattr; 209e690818eStnn const struct ssdfb_product *p; 210e690818eStnn 211e690818eStnn p = ssdfb_lookup_product(flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK); 212e690818eStnn if (p == NULL) { 213e690818eStnn aprint_error(": unknown display assembly\n"); 214e690818eStnn return; 215e690818eStnn } 216e690818eStnn sc->sc_p = p; 217e690818eStnn 218e690818eStnn aprint_naive("\n"); 219e690818eStnn aprint_normal(": %s (%s)\n", 220e690818eStnn ssdfb_controller_names[p->p_controller_id], 221e690818eStnn p->p_name); 222e690818eStnn 223e690818eStnn sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 224e690818eStnn sc->sc_is_console = flags & SSDFB_ATTACH_FLAG_CONSOLE ? true : false; 225e690818eStnn sc->sc_inverse = flags & SSDFB_ATTACH_FLAG_INVERSE ? true : false; 226e690818eStnn sc->sc_upsidedown = flags & SSDFB_ATTACH_FLAG_UPSIDEDOWN ? true : false; 227e690818eStnn sc->sc_backoff = 1; 228e690818eStnn sc->sc_contrast = sc->sc_p->p_default_contrast; 229e690818eStnn sc->sc_gddram_len = sc->sc_p->p_width * sc->sc_p->p_height / 8; 230e690818eStnn sc->sc_gddram = kmem_alloc(sc->sc_gddram_len, KM_SLEEP); 231e690818eStnn if (sc->sc_gddram == NULL) 232e690818eStnn goto out; 233e690818eStnn 234e690818eStnn aprint_normal_dev(sc->sc_dev, "%dx%d%s\n", sc->sc_p->p_width, 235e690818eStnn sc->sc_p->p_height, sc->sc_is_console ? ", console" : ""); 236e690818eStnn 237e690818eStnn /* 238e690818eStnn * Initialize rasops. The native depth is 1-bit monochrome and we 239e690818eStnn * support this in text emul mode via rasops1. But modern Xorg 240e690818eStnn * userland has many rendering glitches when running with 1-bit depth 241e690818eStnn * so to better support this use case we instead declare ourselves as 242e690818eStnn * an 8-bit display with a two entry constant color map. 243e690818eStnn */ 244e690818eStnn error = ssdfb_pick_font(&sc->sc_fontcookie, &sc->sc_font); 245e690818eStnn if (error) { 246e690818eStnn aprint_error_dev(sc->sc_dev, "no font\n"); 247e690818eStnn goto out; 248e690818eStnn } 249e690818eStnn ri->ri_depth = 8; 250e690818eStnn ri->ri_font = sc->sc_font; 251e690818eStnn ri->ri_width = sc->sc_p->p_width; 252e690818eStnn ri->ri_height = sc->sc_p->p_height; 253e690818eStnn ri->ri_stride = ri->ri_width * ri->ri_depth / 8; 254e690818eStnn ri->ri_hw = sc; 255e690818eStnn ri->ri_flg = RI_FULLCLEAR; 256e690818eStnn sc->sc_ri_bits_len = round_page(ri->ri_stride * ri->ri_height); 257e690818eStnn ri->ri_bits = (u_char *)uvm_km_alloc(kernel_map, sc->sc_ri_bits_len, 258e690818eStnn 0, UVM_KMF_WIRED); 259e690818eStnn if (ri->ri_bits == NULL) 260e690818eStnn goto out; 261e690818eStnn 262e690818eStnn error = rasops_init(ri, 263e690818eStnn sc->sc_p->p_height / sc->sc_font->fontheight, 264e690818eStnn sc->sc_p->p_width / sc->sc_font->fontwidth); 265e690818eStnn if (error) 266e690818eStnn goto out; 267e690818eStnn 268e690818eStnn ri->ri_caps &= ~WSSCREEN_WSCOLORS; 269e690818eStnn 270e690818eStnn /* 271e690818eStnn * Save original emul ops & insert our damage notification hooks. 272e690818eStnn */ 273e690818eStnn sc->sc_orig_riops = ri->ri_ops; 274e690818eStnn ri->ri_ops.putchar = ssdfb_putchar; 275e690818eStnn ri->ri_ops.copycols = ssdfb_copycols; 276e690818eStnn ri->ri_ops.erasecols = ssdfb_erasecols; 277e690818eStnn ri->ri_ops.copyrows = ssdfb_copyrows; 278e690818eStnn ri->ri_ops.eraserows = ssdfb_eraserows; 279e690818eStnn ri->ri_ops.cursor = ssdfb_cursor; 280e690818eStnn 281e690818eStnn /* 282e690818eStnn * Set up the screen. 283e690818eStnn */ 284e690818eStnn sc->sc_screen_descr = (struct wsscreen_descr){ 285e690818eStnn .name = "default", 286e690818eStnn .ncols = ri->ri_cols, 287e690818eStnn .nrows = ri->ri_rows, 288e690818eStnn .textops = &ri->ri_ops, 289e690818eStnn .fontwidth = ri->ri_font->fontwidth, 290e690818eStnn .fontheight = ri->ri_font->fontheight, 291e690818eStnn .capabilities = ri->ri_caps 292e690818eStnn }; 293e690818eStnn sc->sc_screens[0] = &sc->sc_screen_descr; 294e690818eStnn sc->sc_screenlist = (struct wsscreen_list){ 295e690818eStnn .nscreens = 1, 296e690818eStnn .screens = sc->sc_screens 297e690818eStnn }; 298e690818eStnn 299e690818eStnn /* 300e690818eStnn * Initialize hardware. 301e690818eStnn */ 302*8e57c9bfStnn error = p->p_init(sc); 303e690818eStnn if (error) 304e690818eStnn goto out; 305e690818eStnn 306e690818eStnn if (sc->sc_is_console) 307e690818eStnn ssdfb_set_usepoll(sc, true); 308e690818eStnn 309e9acf857Stnn mutex_init(&sc->sc_cond_mtx, MUTEX_DEFAULT, IPL_SCHED); 310e690818eStnn cv_init(&sc->sc_cond, "ssdfb"); 311e690818eStnn error = kthread_create(PRI_SOFTCLOCK, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, 312e690818eStnn NULL, ssdfb_thread, sc, &sc->sc_thread, "%s", 313e690818eStnn device_xname(sc->sc_dev)); 314e690818eStnn if (error) { 315e690818eStnn cv_destroy(&sc->sc_cond); 316e690818eStnn mutex_destroy(&sc->sc_cond_mtx); 317e690818eStnn goto out; 318e690818eStnn } 319e690818eStnn 320e690818eStnn /* 321e690818eStnn * Attach wsdisplay. 322e690818eStnn */ 323e690818eStnn if (sc->sc_is_console) { 324e690818eStnn (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 325e690818eStnn wsdisplay_cnattach(&sc->sc_screen_descr, ri, 0, 0, defattr); 326e690818eStnn #if defined(DDB) 327e690818eStnn db_trap_callback = ssdfb_ddb_trap_callback; 328e690818eStnn #endif 329e690818eStnn } 330e690818eStnn aa = (struct wsemuldisplaydev_attach_args){ 331e690818eStnn .console = sc->sc_is_console, 332e690818eStnn .scrdata = &sc->sc_screenlist, 333e690818eStnn .accessops = &ssdfb_accessops, 334e690818eStnn .accesscookie = sc 335e690818eStnn }; 336e690818eStnn sc->sc_wsdisplay = 337e690818eStnn config_found(sc->sc_dev, &aa, wsemuldisplaydevprint); 338e690818eStnn 339e690818eStnn return; 340e690818eStnn out: 341e690818eStnn aprint_error_dev(sc->sc_dev, "attach failed: %d\n", error); 342e690818eStnn if (sc->sc_gddram != NULL) 343e690818eStnn kmem_free(sc->sc_gddram, sc->sc_gddram_len); 344e690818eStnn if (ri->ri_bits != NULL) 345e690818eStnn uvm_km_free(kernel_map, (vaddr_t)ri->ri_bits, sc->sc_ri_bits_len, 346e690818eStnn UVM_KMF_WIRED); 347e690818eStnn if (sc->sc_fontcookie > 0) 348e690818eStnn (void) wsfont_unlock(sc->sc_fontcookie); 349e690818eStnn } 350e690818eStnn 351e690818eStnn int 352e690818eStnn ssdfb_detach(struct ssdfb_softc *sc) 353e690818eStnn { 354e690818eStnn mutex_enter(&sc->sc_cond_mtx); 355e690818eStnn sc->sc_detaching = true; 356e690818eStnn cv_broadcast(&sc->sc_cond); 357e690818eStnn mutex_exit(&sc->sc_cond_mtx); 358e690818eStnn kthread_join(sc->sc_thread); 359e690818eStnn 36033ea73e5Stnn if (sc->sc_uobj != NULL) { 36133ea73e5Stnn mutex_enter(sc->sc_uobj->vmobjlock); 36233ea73e5Stnn sc->sc_uobj->uo_refs--; 36333ea73e5Stnn mutex_exit(sc->sc_uobj->vmobjlock); 36433ea73e5Stnn } 365e690818eStnn config_detach(sc->sc_wsdisplay, DETACH_FORCE); 366e690818eStnn 367e690818eStnn cv_destroy(&sc->sc_cond); 368e690818eStnn mutex_destroy(&sc->sc_cond_mtx); 369e690818eStnn uvm_km_free(kernel_map, (vaddr_t)sc->sc_ri.ri_bits, sc->sc_ri_bits_len, 370e690818eStnn UVM_KMF_WIRED); 371e690818eStnn kmem_free(sc->sc_gddram, sc->sc_gddram_len); 372e690818eStnn (void) wsfont_unlock(sc->sc_fontcookie); 373e690818eStnn return 0; 374e690818eStnn } 375e690818eStnn 376e690818eStnn static int 377e690818eStnn ssdfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) 378e690818eStnn { 379e690818eStnn struct ssdfb_softc *sc = v; 380e690818eStnn struct wsdisplay_param *wdp; 381e690818eStnn struct wsdisplay_cmap *wc; 382e690818eStnn u_char cmap[] = {0, 0xff}; 383e690818eStnn int error; 384e690818eStnn 385e690818eStnn switch (cmd) { 386e690818eStnn case WSDISPLAYIO_GTYPE: 387e690818eStnn *(u_int *)data = WSDISPLAY_TYPE_SSDFB; 388e690818eStnn return 0; 389e690818eStnn case WSDISPLAYIO_GINFO: 390e690818eStnn *(struct wsdisplay_fbinfo *)data = (struct wsdisplay_fbinfo){ 391e690818eStnn .width = sc->sc_ri.ri_width, 392e690818eStnn .height = sc->sc_ri.ri_height, 393e690818eStnn .depth = sc->sc_ri.ri_depth, 394e690818eStnn .cmsize = 2 395e690818eStnn }; 396e690818eStnn return 0; 397e690818eStnn case WSDISPLAYIO_GET_FBINFO: 398e690818eStnn return wsdisplayio_get_fbinfo(&sc->sc_ri, 399e690818eStnn (struct wsdisplayio_fbinfo *)data); 400e690818eStnn case WSDISPLAYIO_LINEBYTES: 401e690818eStnn *(u_int *)data = sc->sc_ri.ri_stride; 402e690818eStnn return 0; 403e690818eStnn case WSDISPLAYIO_GETPARAM: 404e690818eStnn wdp = (struct wsdisplay_param *)data; 405e690818eStnn if (wdp->param != WSDISPLAYIO_PARAM_CONTRAST) 406e690818eStnn return EINVAL; 407e690818eStnn wdp->min = 0; 408e690818eStnn wdp->max = 0xff; 409e690818eStnn wdp->curval = sc->sc_contrast; 410e690818eStnn return 0; 411e690818eStnn case WSDISPLAYIO_SETPARAM: 412e690818eStnn wdp = (struct wsdisplay_param *)data; 413e690818eStnn if (wdp->param != WSDISPLAYIO_PARAM_CONTRAST) 414e690818eStnn return EINVAL; 415e690818eStnn if (wdp->curval < 0 || wdp->curval > 0xff) 416e690818eStnn return EINVAL; 417e690818eStnn return ssdfb_set_contrast(sc, wdp->curval, sc->sc_usepoll); 418e690818eStnn case WSDISPLAYIO_GMODE: 419e690818eStnn *(u_int *)data = sc->sc_mode; 420e690818eStnn return 0; 421e690818eStnn case WSDISPLAYIO_SMODE: 422e690818eStnn return ssdfb_set_mode(sc, *(u_int *)data); 423e690818eStnn case WSDISPLAYIO_GVIDEO: 424e690818eStnn *(u_int *)data = sc->sc_display_on 425e690818eStnn ? WSDISPLAYIO_VIDEO_ON 426e690818eStnn : WSDISPLAYIO_VIDEO_OFF; 427e690818eStnn return 0; 428e690818eStnn case WSDISPLAYIO_SVIDEO: 429e690818eStnn switch (*(u_int *)data) { 430e690818eStnn case WSDISPLAYIO_VIDEO_ON: 431e690818eStnn case WSDISPLAYIO_VIDEO_OFF: 432e690818eStnn break; 433e690818eStnn default: 434e690818eStnn return EINVAL; 435e690818eStnn } 436e690818eStnn return ssdfb_set_display_on(sc, 437e690818eStnn *(u_int *)data == WSDISPLAYIO_VIDEO_ON ? true : false, 438e690818eStnn sc->sc_usepoll); 439e690818eStnn #if 0 /* don't let userland mess with polling yet */ 440e690818eStnn case WSDISPLAYIO_SET_POLLING: 441e690818eStnn switch (*(u_int *)data) { 442e690818eStnn case 0: 443e690818eStnn case 1: 444e690818eStnn break; 445e690818eStnn default: 446e690818eStnn return EINVAL; 447e690818eStnn } 448e690818eStnn mutex_enter(&sc->sc_cond_mtx); 449e690818eStnn ssdfb_set_usepoll(sc, *(u_int *)data ? true : false); 450e690818eStnn cv_broadcast(&sc->sc_cond); 451e690818eStnn mutex_exit(&sc->sc_cond_mtx); 452e690818eStnn return 0; 453e690818eStnn #endif 454e690818eStnn case WSDISPLAYIO_GETCMAP: 455e690818eStnn wc = (struct wsdisplay_cmap *)data; 456e690818eStnn if (wc->index >= __arraycount(cmap) || 45733ea73e5Stnn wc->count > __arraycount(cmap) - wc->index) 458e690818eStnn return EINVAL; 459e690818eStnn error = copyout(&cmap[wc->index], wc->red, wc->count); 460e690818eStnn if (error) 461e690818eStnn return error; 462e690818eStnn error = copyout(&cmap[wc->index], wc->green, wc->count); 463e690818eStnn if (error) 464e690818eStnn return error; 465e690818eStnn error = copyout(&cmap[wc->index], wc->blue, wc->count); 466e690818eStnn return error; 467e690818eStnn case WSDISPLAYIO_PUTCMAP: 468e690818eStnn return ENODEV; 469e690818eStnn } 470e690818eStnn 471e690818eStnn return EPASSTHROUGH; 472e690818eStnn } 473e690818eStnn 474e690818eStnn static paddr_t 475e690818eStnn ssdfb_mmap(void *v, void *vs, off_t off, int prot) 476e690818eStnn { 477e690818eStnn struct ssdfb_softc *sc = (struct ssdfb_softc *)v; 478e690818eStnn struct rasops_info *ri = &sc->sc_ri; 479e690818eStnn vaddr_t va_base = (vaddr_t)ri->ri_bits; 480e690818eStnn paddr_t pa; 481e690818eStnn 482e690818eStnn if (off < 0 || off >= sc->sc_ri_bits_len || (off & PAGE_MASK) != 0) 483e690818eStnn return -1; 484e690818eStnn 485e690818eStnn if (!pmap_extract(pmap_kernel(), va_base + off, &pa)) 486e690818eStnn return -1; 487e690818eStnn 488e690818eStnn return atop(pa); 489e690818eStnn } 490e690818eStnn 491e690818eStnn static int 492e690818eStnn ssdfb_alloc_screen(void *v, const struct wsscreen_descr *descr, void **cookiep, 493e690818eStnn int *curxp, int *curyp, long *attrp) 494e690818eStnn { 495e690818eStnn struct ssdfb_softc *sc = v; 496e690818eStnn struct rasops_info *ri = &sc->sc_ri; 497e690818eStnn 498e690818eStnn if (sc->sc_nscreens > 0) 499e690818eStnn return ENOMEM; 500e690818eStnn 501e690818eStnn ri->ri_ops.allocattr(ri, 0, 0, 0, attrp); 502e690818eStnn *cookiep = &sc->sc_ri; 503e690818eStnn *curxp = 0; 504e690818eStnn *curyp = 0; 505e690818eStnn sc->sc_nscreens++; 506e690818eStnn 507e690818eStnn return 0; 508e690818eStnn } 509e690818eStnn 510e690818eStnn static void 511e690818eStnn ssdfb_free_screen(void *v, void *cookie) 512e690818eStnn { 513e690818eStnn struct ssdfb_softc *sc = v; 514e690818eStnn 515e690818eStnn if (sc->sc_is_console) 516e690818eStnn panic("ssdfb_free_screen: is console"); 517e690818eStnn 518e690818eStnn sc->sc_nscreens--; 519e690818eStnn } 520e690818eStnn 521e690818eStnn static int 522e690818eStnn ssdfb_show_screen(void *v, void *cookie, int waitok, 523e690818eStnn void (*cb) (void *, int, int), void *cb_arg) 524e690818eStnn { 525e690818eStnn return 0; 526e690818eStnn } 527e690818eStnn 528e690818eStnn static void 529e690818eStnn ssdfb_putchar(void *cookie, int row, int col, u_int c, long attr) 530e690818eStnn { 531e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie; 532e690818eStnn struct ssdfb_softc *sc = ri->ri_hw; 533e690818eStnn 534e690818eStnn sc->sc_orig_riops.putchar(cookie, row, col, c, attr); 535e690818eStnn ssdfb_damage(sc); 536e690818eStnn } 537e690818eStnn 538e690818eStnn static void 539e690818eStnn ssdfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 540e690818eStnn { 541e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie; 542e690818eStnn struct ssdfb_softc *sc = ri->ri_hw; 543e690818eStnn 544e690818eStnn sc->sc_orig_riops.copycols(cookie, row, srccol, dstcol, ncols); 545e690818eStnn ssdfb_damage(sc); 546e690818eStnn } 547e690818eStnn 548e690818eStnn static void 549e690818eStnn ssdfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 550e690818eStnn { 551e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie; 552e690818eStnn struct ssdfb_softc *sc = ri->ri_hw; 553e690818eStnn 554e690818eStnn sc->sc_orig_riops.erasecols(cookie, row, startcol, ncols, fillattr); 555e690818eStnn ssdfb_damage(sc); 556e690818eStnn } 557e690818eStnn 558e690818eStnn static void 559e690818eStnn ssdfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 560e690818eStnn { 561e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie; 562e690818eStnn struct ssdfb_softc *sc = ri->ri_hw; 563e690818eStnn 564e690818eStnn sc->sc_orig_riops.copyrows(cookie, srcrow, dstrow, nrows); 565e690818eStnn ssdfb_damage(sc); 566e690818eStnn } 567e690818eStnn 568e690818eStnn static void 569e690818eStnn ssdfb_eraserows(void *cookie, int row, int nrows, long fillattr) 570e690818eStnn { 571e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie; 572e690818eStnn struct ssdfb_softc *sc = ri->ri_hw; 573e690818eStnn 574e690818eStnn sc->sc_orig_riops.eraserows(cookie, row, nrows, fillattr); 575e690818eStnn ssdfb_damage(sc); 576e690818eStnn } 577e690818eStnn 578e690818eStnn static void 579e690818eStnn ssdfb_cursor(void *cookie, int on, int row, int col) 580e690818eStnn { 581e690818eStnn struct rasops_info *ri = (struct rasops_info *)cookie; 582e690818eStnn struct ssdfb_softc *sc = ri->ri_hw; 583e690818eStnn 584e690818eStnn sc->sc_orig_riops.cursor(cookie, on, row, col); 585e690818eStnn ssdfb_damage(sc); 586e690818eStnn } 587e690818eStnn 588e690818eStnn static int 589*8e57c9bfStnn ssdfb_init_ssd1306(struct ssdfb_softc *sc) 590e690818eStnn { 591e690818eStnn int error; 59233ea73e5Stnn uint8_t cmd[2]; 593e690818eStnn bool usepoll = true; 594e690818eStnn 595e690818eStnn /* 596e690818eStnn * Enter sleep. 597e690818eStnn */ 598e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_DISPLAY_OFF); 599e690818eStnn if (error) 600e690818eStnn return error; 601e690818eStnn SSDFB_CMD1(SSDFB_CMD_DEACTIVATE_SCROLL); 602e690818eStnn if (error) 603e690818eStnn return error; 604e690818eStnn SSDFB_CMD1(SSDFB_CMD_ENTIRE_DISPLAY_OFF); 605e690818eStnn if (error) 606e690818eStnn return error; 607e690818eStnn 608e690818eStnn /* 609e690818eStnn * Configure physical display panel layout. 610e690818eStnn */ 611e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_MULTIPLEX_RATIO, sc->sc_p->p_multiplex_ratio); 612e690818eStnn if (error) 613e690818eStnn return error; 614e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_DISPLAY_OFFSET, 0); 615e690818eStnn if (error) 616e690818eStnn return error; 617e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_DISPLAY_START_LINE_BASE + 0x00); 618e690818eStnn if (error) 619e690818eStnn return error; 620e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_COM_PINS_HARDWARE_CFG, sc->sc_p->p_compin_cfg); 621e690818eStnn if (error) 622e690818eStnn return error; 623e690818eStnn if (sc->sc_upsidedown) { 624e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_SEGMENT_REMAP_REVERSE); 625e690818eStnn if (error) 626e690818eStnn return error; 627e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_REMAP); 628e690818eStnn if (error) 629e690818eStnn return error; 630e690818eStnn } else { 631e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_SEGMENT_REMAP_NORMAL); 632e690818eStnn if (error) 633e690818eStnn return error; 634e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_COM_OUTPUT_DIRECTION_NORMAL); 635e690818eStnn if (error) 636e690818eStnn return error; 637e690818eStnn } 638e690818eStnn SSDFB_CMD1(SSDFB_CMD_SET_NORMAL_DISPLAY + (uint8_t)sc->sc_inverse); 639e690818eStnn if (error) 640e690818eStnn return error; 641e690818eStnn 642e690818eStnn /* 643e690818eStnn * Configure timing characteristics. 644e690818eStnn */ 645e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_DISPLAY_CLOCK_RATIO, 64609fa88a1Stnn __SHIFTIN(sc->sc_p->p_fosc, SSDFB_DISPLAY_CLOCK_OSCILLATOR_MASK) | 64709fa88a1Stnn __SHIFTIN(sc->sc_p->p_fosc_div, SSDFB_DISPLAY_CLOCK_DIVIDER_MASK)); 648e690818eStnn if (error) 649e690818eStnn return error; 650e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_CONTRAST_CONTROL, sc->sc_contrast); 651e690818eStnn if (error) 652e690818eStnn return error; 653e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_PRECHARGE_PERIOD, 65409fa88a1Stnn __SHIFTIN(sc->sc_p->p_precharge, SSDFB_PRECHARGE_MASK) | 65509fa88a1Stnn __SHIFTIN(sc->sc_p->p_discharge, SSDFB_DISCHARGE_MASK)); 656e690818eStnn if (error) 657e690818eStnn return error; 658e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_VCOMH_DESELECT_LEVEL, 659e690818eStnn sc->sc_p->p_vcomh_deselect_level); 660e690818eStnn if (error) 661e690818eStnn return error; 662e690818eStnn 663e690818eStnn /* 664e690818eStnn * Start charge pump. 665e690818eStnn */ 666e690818eStnn SSDFB_CMD2(sc->sc_p->p_chargepump_cmd, sc->sc_p->p_chargepump_arg); 667e690818eStnn if (error) 668e690818eStnn return error; 669e690818eStnn 670e690818eStnn if (sc->sc_p->p_controller_id == SSDFB_CONTROLLER_SH1106) { 671e690818eStnn SSDFB_CMD2(SH1106_CMD_SET_DC_DC, SH1106_DC_DC_ON); 672e690818eStnn if (error) 673e690818eStnn return error; 674e690818eStnn } 675e690818eStnn 676e690818eStnn ssdfb_clear_screen(sc); 677e690818eStnn error = ssdfb_sync(sc, usepoll); 678e690818eStnn if (error) 679e690818eStnn return error; 680e690818eStnn error = ssdfb_set_display_on(sc, true, usepoll); 681e690818eStnn 682e690818eStnn return error; 683e690818eStnn } 684e690818eStnn 685e690818eStnn static int 686e690818eStnn ssdfb_set_contrast(struct ssdfb_softc *sc, uint8_t value, bool usepoll) 687e690818eStnn { 688e690818eStnn uint8_t cmd[2]; 689e690818eStnn int error; 690e690818eStnn 691e690818eStnn sc->sc_contrast = value; 692e690818eStnn SSDFB_CMD2(SSDFB_CMD_SET_CONTRAST_CONTROL, value); 693e690818eStnn 694e690818eStnn return error; 695e690818eStnn } 696e690818eStnn 697e690818eStnn static int 698e690818eStnn ssdfb_set_display_on(struct ssdfb_softc *sc, bool value, bool usepoll) 699e690818eStnn { 700e690818eStnn uint8_t cmd[1]; 701e690818eStnn int error; 702e690818eStnn sc->sc_display_on = value; 703e690818eStnn 704e690818eStnn SSDFB_CMD1(value ? SSDFB_CMD_SET_DISPLAY_ON : SSDFB_CMD_SET_DISPLAY_OFF); 705e690818eStnn 706e690818eStnn return error; 707e690818eStnn } 708e690818eStnn 709e690818eStnn static int 710e690818eStnn ssdfb_set_mode(struct ssdfb_softc *sc, u_int mode) 711e690818eStnn { 712e690818eStnn switch (mode) { 713e690818eStnn case WSDISPLAYIO_MODE_EMUL: 714e690818eStnn case WSDISPLAYIO_MODE_DUMBFB: 715e690818eStnn break; 716e690818eStnn default: 717e690818eStnn return EINVAL; 718e690818eStnn } 719e690818eStnn if (mode == sc->sc_mode) 720e690818eStnn return 0; 721e690818eStnn mutex_enter(&sc->sc_cond_mtx); 722e690818eStnn sc->sc_mode = mode; 723e690818eStnn cv_broadcast(&sc->sc_cond); 724e690818eStnn mutex_exit(&sc->sc_cond_mtx); 725e690818eStnn ssdfb_clear_screen(sc); 726e690818eStnn ssdfb_damage(sc); 727e690818eStnn 728e690818eStnn return 0; 729e690818eStnn } 730e690818eStnn 731e690818eStnn static void 732e690818eStnn ssdfb_damage(struct ssdfb_softc *sc) 733e690818eStnn { 734e690818eStnn int s; 735e690818eStnn 736e690818eStnn if (sc->sc_usepoll) { 737e690818eStnn (void) ssdfb_sync(sc, true); 738e690818eStnn } else { 739e690818eStnn /* 740e690818eStnn * kernel code isn't permitted to call us via kprintf at 741e690818eStnn * splhigh. In case misbehaving code calls us anyway we can't 742e690818eStnn * safely take the mutex so we skip the damage notification. 743e690818eStnn */ 744e690818eStnn if (sc->sc_is_console) { 745e690818eStnn s = splhigh(); 746e690818eStnn splx(s); 747e690818eStnn if (s == IPL_HIGH) 748e690818eStnn return; 749e690818eStnn } 750e690818eStnn mutex_enter(&sc->sc_cond_mtx); 751e690818eStnn sc->sc_modified = true; 752e690818eStnn cv_broadcast(&sc->sc_cond); 753e690818eStnn mutex_exit(&sc->sc_cond_mtx); 754e690818eStnn } 755e690818eStnn } 756e690818eStnn 75733ea73e5Stnn static void 75833ea73e5Stnn ssdfb_udv_attach(struct ssdfb_softc *sc) 75933ea73e5Stnn { 76033ea73e5Stnn extern const struct cdevsw wsdisplay_cdevsw; 76133ea73e5Stnn dev_t dev; 76233ea73e5Stnn #define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen)) 76333ea73e5Stnn dev = makedev(cdevsw_lookup_major(&wsdisplay_cdevsw), 76433ea73e5Stnn WSDISPLAYMINOR(device_unit(sc->sc_wsdisplay), 0)); 76533ea73e5Stnn sc->sc_uobj = udv_attach(dev, VM_PROT_READ|VM_PROT_WRITE, 0, 76633ea73e5Stnn sc->sc_ri_bits_len); 76733ea73e5Stnn } 76833ea73e5Stnn 769e690818eStnn static bool 770e690818eStnn ssdfb_is_modified(struct ssdfb_softc *sc) 771e690818eStnn { 772e690818eStnn vaddr_t va, va_end; 773e690818eStnn 774e690818eStnn if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) 775e690818eStnn return sc->sc_modified; 776e690818eStnn 77733ea73e5Stnn if (sc->sc_uobj == NULL) 77833ea73e5Stnn return false; 77933ea73e5Stnn 780e690818eStnn va = (vaddr_t)sc->sc_ri.ri_bits; 781e690818eStnn va_end = va + sc->sc_ri_bits_len; 782e690818eStnn while (va < va_end) { 783e690818eStnn if (pmap_is_modified(uvm_pageratop(va))) 784e690818eStnn return true; 785e690818eStnn va += PAGE_SIZE; 786e690818eStnn } 787e690818eStnn 788e690818eStnn return false; 789e690818eStnn } 790e690818eStnn 791e690818eStnn static bool 792e690818eStnn ssdfb_clear_modify(struct ssdfb_softc *sc) 793e690818eStnn { 794e690818eStnn vaddr_t va, va_end; 795e690818eStnn bool ret; 796e690818eStnn 797e690818eStnn if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) { 798e690818eStnn ret = sc->sc_modified; 799e690818eStnn sc->sc_modified = false; 800e690818eStnn return ret; 801e690818eStnn } 802e690818eStnn 80333ea73e5Stnn if (sc->sc_uobj == NULL) 80433ea73e5Stnn return false; 80533ea73e5Stnn 806e690818eStnn va = (vaddr_t)sc->sc_ri.ri_bits; 807e690818eStnn va_end = va + sc->sc_ri_bits_len; 808e690818eStnn ret = false; 809e690818eStnn while (va < va_end) { 810e690818eStnn if (pmap_clear_modify(uvm_pageratop(va))) 811e690818eStnn ret = true; 812e690818eStnn va += PAGE_SIZE; 813e690818eStnn } 814e690818eStnn 815e690818eStnn return ret; 816e690818eStnn } 817e690818eStnn 818e690818eStnn static void 8192f60d4f5Stnn ssdfb_thread(void *arg) 8202f60d4f5Stnn { 821e690818eStnn struct ssdfb_softc *sc = (struct ssdfb_softc *)arg; 822e690818eStnn int error; 823e690818eStnn 824e690818eStnn mutex_enter(&sc->sc_cond_mtx); 825e690818eStnn 826e690818eStnn if (sc->sc_usepoll) 827e690818eStnn ssdfb_set_usepoll(sc, false); 828e690818eStnn 829e690818eStnn while(!sc->sc_detaching) { 83033ea73e5Stnn if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB && 83133ea73e5Stnn sc->sc_uobj == NULL) { 83233ea73e5Stnn mutex_exit(&sc->sc_cond_mtx); 83333ea73e5Stnn ssdfb_udv_attach(sc); 83433ea73e5Stnn mutex_enter(&sc->sc_cond_mtx); 83533ea73e5Stnn } 836e690818eStnn if (!ssdfb_is_modified(sc)) { 837e690818eStnn if (cv_timedwait(&sc->sc_cond, &sc->sc_cond_mtx, 838e690818eStnn sc->sc_mode == WSDISPLAYIO_MODE_EMUL 839e690818eStnn ? 0 : sc->sc_backoff) == EWOULDBLOCK 840e690818eStnn && sc->sc_backoff < mstohz(200)) { 841e690818eStnn sc->sc_backoff <<= 1; 842e690818eStnn } 843e690818eStnn continue; 844e690818eStnn } 845e690818eStnn sc->sc_backoff = 1; 846e690818eStnn (void) ssdfb_clear_modify(sc); 847e690818eStnn if (sc->sc_usepoll) 848e690818eStnn continue; 849e690818eStnn mutex_exit(&sc->sc_cond_mtx); 850e690818eStnn error = ssdfb_sync(sc, false); 851e690818eStnn if (error) 852e690818eStnn device_printf(sc->sc_dev, "ssdfb_sync: error %d\n", 853e690818eStnn error); 854e690818eStnn mutex_enter(&sc->sc_cond_mtx); 855e690818eStnn } 856e690818eStnn 857e690818eStnn mutex_exit(&sc->sc_cond_mtx); 85850090521Stnn kthread_exit(0); 859e690818eStnn } 860e690818eStnn 861e690818eStnn static void 8622f60d4f5Stnn ssdfb_set_usepoll(struct ssdfb_softc *sc, bool enable) 8632f60d4f5Stnn { 864e690818eStnn sc->sc_usepoll = enable; 865e690818eStnn } 866e690818eStnn 867e690818eStnn static int 8682f60d4f5Stnn ssdfb_sync(struct ssdfb_softc *sc, bool usepoll) 8692f60d4f5Stnn { 870e690818eStnn struct rasops_info *ri = &sc->sc_ri; 871e690818eStnn int block_size = 8; 872e690818eStnn int ri_block_stride = ri->ri_stride * block_size; 873e690818eStnn int height_in_blocks = sc->sc_p->p_height / block_size; 874e690818eStnn int width_in_blocks = sc->sc_p->p_width / block_size; 875e690818eStnn int ri_block_step = block_size * ri->ri_depth / 8; 876e690818eStnn int x, y; 877e690818eStnn union ssdfb_block *blockp; 878e690818eStnn uint64_t raw_block; 879e690818eStnn uint8_t *src; 880e690818eStnn int x1, x2, y1, y2; 881e690818eStnn 882e690818eStnn /* 883e690818eStnn * Transfer rasops bitmap into gddram shadow buffer while keeping track 884e690818eStnn * of the bounding box of the dirty region we scribbled over. 885e690818eStnn */ 886e690818eStnn x1 = width_in_blocks; 887e690818eStnn x2 = -1; 888e690818eStnn y1 = height_in_blocks; 889e690818eStnn y2 = -1; 890e690818eStnn for (y = 0; y < height_in_blocks; y++) { 891e690818eStnn src = &ri->ri_bits[y*ri_block_stride]; 892e690818eStnn blockp = &sc->sc_gddram[y * width_in_blocks]; 893e690818eStnn for (x = 0; x < width_in_blocks; x++) { 894e690818eStnn raw_block = (ri->ri_depth == 1) 895e690818eStnn ? ssdfb_transpose_block_1bpp(src, ri->ri_stride) 896e690818eStnn : ssdfb_transpose_block_8bpp(src, ri->ri_stride); 897e690818eStnn if (raw_block != blockp->raw) { 898e690818eStnn blockp->raw = raw_block; 899e690818eStnn if (x1 > x) 900e690818eStnn x1 = x; 901e690818eStnn if (x2 < x) 902e690818eStnn x2 = x; 903e690818eStnn if (y1 > y) 904e690818eStnn y1 = y; 905e690818eStnn if (y2 < y) 906e690818eStnn y2 = y; 907e690818eStnn } 908e690818eStnn src += ri_block_step; 909e690818eStnn blockp++; 910e690818eStnn } 911e690818eStnn } 912e690818eStnn if (x2 != -1) 913e690818eStnn return sc->sc_transfer_rect(sc->sc_cookie, 914e690818eStnn x1 * block_size + sc->sc_p->p_panel_shift, 915e690818eStnn (x2 + 1) * block_size - 1 + sc->sc_p->p_panel_shift, 916e690818eStnn y1, 917e690818eStnn y2, 918e690818eStnn &sc->sc_gddram[y1 * width_in_blocks + x1].col[0], 919e690818eStnn sc->sc_p->p_width, 920e690818eStnn usepoll); 921e690818eStnn 922e690818eStnn return 0; 923e690818eStnn } 924e690818eStnn 925e690818eStnn static uint64_t 926e690818eStnn ssdfb_transpose_block_1bpp(uint8_t *src, size_t src_stride) 927e690818eStnn { 928e690818eStnn uint64_t x = 0; 929e690818eStnn uint64_t t; 930e690818eStnn int i; 931e690818eStnn 932e690818eStnn /* 933e690818eStnn * collect the 8x8 block. 934e690818eStnn */ 935e690818eStnn for (i = 0; i < 8; i++) { 936e690818eStnn x >>= 8; 937e690818eStnn x |= (uint64_t)src[i * src_stride] << 56; 938e690818eStnn } 939e690818eStnn 940e690818eStnn /* 941e690818eStnn * Transpose it into gddram layout. 942e690818eStnn * Post-transpose bswap is the same as pre-transpose bit order reversal. 943e690818eStnn * We do this to match rasops1 bit order. 944e690818eStnn */ 945e690818eStnn t = (x ^ (x >> 28)) & 0x00000000F0F0F0F0ULL; 946e690818eStnn x = x ^ t ^ (t << 28); 947e690818eStnn t = (x ^ (x >> 14)) & 0x0000CCCC0000CCCCULL; 948e690818eStnn x = x ^ t ^ (t << 14); 949e690818eStnn t = (x ^ (x >> 7)) & 0x00AA00AA00AA00AAULL; 950e690818eStnn x = x ^ t ^ (t << 7); 951e690818eStnn x = bswap64(x); 952e690818eStnn 953e690818eStnn return htole64(x); 954e690818eStnn } 955e690818eStnn 956e690818eStnn static uint64_t 957e690818eStnn ssdfb_transpose_block_8bpp(uint8_t *src, size_t src_stride) 958e690818eStnn { 959e690818eStnn uint64_t x = 0; 960e690818eStnn int m, n; 961e690818eStnn 962e690818eStnn for (m = 0; m < 8; m++) { 963e690818eStnn for (n = 0; n < 8; n++) { 964e690818eStnn x >>= 1; 965e690818eStnn x |= src[n * src_stride + m] ? (1ULL << 63) : 0; 966e690818eStnn } 967e690818eStnn } 968e690818eStnn 969e690818eStnn return htole64(x); 970e690818eStnn } 971e690818eStnn 972e690818eStnn static const struct ssdfb_product * 9732f60d4f5Stnn ssdfb_lookup_product(ssdfb_product_id_t id) 9742f60d4f5Stnn { 975e690818eStnn int i; 976e690818eStnn 977e690818eStnn for (i = 0; i < __arraycount(ssdfb_products); i++) { 978e690818eStnn if (ssdfb_products[i].p_product_id == id) 979e690818eStnn return &ssdfb_products[i]; 980e690818eStnn } 981e690818eStnn 982e690818eStnn return NULL; 983e690818eStnn } 984e690818eStnn 985e690818eStnn static int 9862f60d4f5Stnn ssdfb_pick_font(int *cookiep, struct wsdisplay_font **fontp) 9872f60d4f5Stnn { 988e690818eStnn int error; 989e690818eStnn int c; 990e690818eStnn struct wsdisplay_font *f; 991e690818eStnn int i; 992e690818eStnn uint8_t d[4][2] = {{5, 8}, {8, 8}, {8, 10} ,{8, 16}}; 993e690818eStnn 994e690818eStnn /* 9956f6ddff5Stnn * Try to find fonts in order of increasing size. 996e690818eStnn */ 997e690818eStnn wsfont_init(); 998e690818eStnn for(i = 0; i < __arraycount(d); i++) { 999e690818eStnn c = wsfont_find(NULL, d[i][0], d[i][1], 0, 1000e690818eStnn WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R, 1001e690818eStnn WSFONT_FIND_BITMAP); 1002e690818eStnn if (c > 0) 1003e690818eStnn break; 1004e690818eStnn } 1005e690818eStnn if (c <= 0) 1006e690818eStnn return ENOENT; 1007e690818eStnn error = wsfont_lock(c, &f); 1008e690818eStnn if (error) 1009e690818eStnn return error; 1010e690818eStnn *cookiep = c; 1011e690818eStnn *fontp = f; 1012e690818eStnn 1013e690818eStnn return 0; 1014e690818eStnn } 1015e690818eStnn 1016e690818eStnn static void 1017e690818eStnn ssdfb_clear_screen(struct ssdfb_softc *sc) 1018e690818eStnn { 1019e690818eStnn struct rasops_info *ri = &sc->sc_ri; 1020e690818eStnn 1021e690818eStnn memset(sc->sc_gddram, 0xff, sc->sc_gddram_len); 1022e690818eStnn memset(ri->ri_bits, 0, sc->sc_ri_bits_len); 1023e690818eStnn } 1024e690818eStnn 1025e690818eStnn #if defined(DDB) 1026e690818eStnn static void 1027e690818eStnn ssdfb_ddb_trap_callback(int enable) 1028e690818eStnn { 1029e690818eStnn extern struct cfdriver ssdfb_cd; 1030e690818eStnn struct ssdfb_softc *sc; 1031e690818eStnn int i; 1032e690818eStnn 1033e690818eStnn for (i = 0; i < ssdfb_cd.cd_ndevs; i++) { 1034e690818eStnn sc = device_lookup_private(&ssdfb_cd, i); 1035e690818eStnn if (sc != NULL && sc->sc_is_console) { 1036e690818eStnn ssdfb_set_usepoll(sc, (bool)enable); 1037e690818eStnn } 1038e690818eStnn } 1039e690818eStnn } 1040e690818eStnn #endif 1041