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