1 /* $NetBSD: udl.c,v 1.17 2016/10/18 20:17:37 nat Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 FUKAUMI Naoki. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org> 30 * 31 * Permission to use, copy, modify, and distribute this software for any 32 * purpose with or without fee is hereby granted, provided that the above 33 * copyright notice and this permission notice appear in all copies. 34 * 35 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 36 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 37 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 38 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 39 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 40 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 41 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 42 */ 43 44 /* 45 * Driver for the ``DisplayLink DL-1x0 / DL-1x5'' graphic chips based 46 * on the reversed engineered specifications of Florian Echtler 47 * <floe at butterbrot dot org>: 48 * 49 * http://floe.butterbrot.org/displaylink/doku.php 50 * 51 * This driver was written by Marcus Glocker for OpenBSD and ported to 52 * NetBSD by FUKAUMI Naoki with many modification. 53 */ 54 55 #include <sys/cdefs.h> 56 __KERNEL_RCSID(0, "$NetBSD: udl.c,v 1.17 2016/10/18 20:17:37 nat Exp $"); 57 58 #include <sys/param.h> 59 #include <sys/device.h> 60 #include <sys/kernel.h> 61 #include <sys/proc.h> 62 #include <sys/systm.h> 63 #include <sys/kmem.h> 64 #include <sys/kthread.h> 65 #include <sys/condvar.h> 66 #include <uvm/uvm.h> 67 68 #include <sys/bus.h> 69 #include <sys/endian.h> 70 71 #include <dev/usb/usb.h> 72 #include <dev/usb/usbdi.h> 73 #include <dev/usb/usbdivar.h> 74 #include <dev/usb/usbdi_util.h> 75 #include <dev/usb/usb_mem.h> 76 #include <dev/usb/usbdevs.h> 77 78 #include <dev/firmload.h> 79 80 #include <dev/videomode/videomode.h> 81 #include <dev/videomode/edidvar.h> 82 83 #include <dev/wscons/wsconsio.h> 84 #include <dev/wscons/wsdisplayvar.h> 85 #include <dev/rasops/rasops.h> 86 87 #include <dev/usb/udl.h> 88 #ifdef notyet 89 #include <dev/usb/udlio.h> 90 #endif 91 92 /* 93 * Defines. 94 */ 95 #ifdef UDL_DEBUG 96 #define DPRINTF(x) do { if (udl_debug) printf x; } while (0) 97 #define DPRINTFN(n, x) do { if (udl_debug >= (n)) printf x; } while (0) 98 int udl_debug = 1; 99 #else 100 #define DPRINTF(x) do {} while (0) 101 #define DPRINTFN(n, x) do {} while (0) 102 #endif 103 104 /* 105 * Prototypes. 106 */ 107 static int udl_match(device_t, cfdata_t, void *); 108 static void udl_attach(device_t, device_t, void *); 109 static int udl_detach(device_t, int); 110 111 static int udl_ioctl(void *, void *, u_long, void *, int, 112 struct lwp *); 113 static paddr_t udl_mmap(void *, void *, off_t, int); 114 static int udl_alloc_screen(void *, const struct wsscreen_descr *, 115 void **, int *, int *, long *); 116 static void udl_free_screen(void *, void *); 117 static int udl_show_screen(void *, void *, int, 118 void (*)(void *, int, int), void *); 119 120 static void udl_comp_load(struct udl_softc *); 121 static void udl_comp_unload(struct udl_softc *); 122 static int udl_fbmem_alloc(struct udl_softc *); 123 static void udl_fbmem_free(struct udl_softc *); 124 static int udl_cmdq_alloc(struct udl_softc *); 125 static void udl_cmdq_free(struct udl_softc *); 126 static struct udl_cmdq *udl_cmdq_get(struct udl_softc *sc); 127 static void udl_cmdq_put(struct udl_softc *sc, 128 struct udl_cmdq *cmdq); 129 static void udl_cmdq_flush(struct udl_softc *); 130 131 static void udl_cursor(void *, int, int, int); 132 static void udl_putchar(void *, int, int, u_int, long); 133 static void udl_copycols(void *, int, int, int, int); 134 static void udl_erasecols(void *, int, int, int, long); 135 static void udl_copyrows(void *, int, int, int); 136 static void udl_eraserows(void *, int, int, long); 137 138 static void udl_restore_char(struct rasops_info *); 139 static void udl_draw_char(struct rasops_info *, uint16_t *, u_int, 140 int, int); 141 static void udl_copy_rect(struct udl_softc *, int, int, int, int, 142 int, int); 143 static void udl_fill_rect(struct udl_softc *, uint16_t, int, int, 144 int, int); 145 #ifdef notyet 146 static void udl_draw_rect(struct udl_softc *, 147 struct udl_ioctl_damage *); 148 static void udl_draw_rect_comp(struct udl_softc *, 149 struct udl_ioctl_damage *); 150 #endif 151 152 static inline void udl_copy_line(struct udl_softc *, int, int, int); 153 static inline void udl_fill_line(struct udl_softc *, uint16_t, int, int); 154 static inline void udl_draw_line(struct udl_softc *, uint16_t *, int, 155 int); 156 #ifdef notyet 157 static inline void udl_draw_line_comp(struct udl_softc *, uint16_t *, int, 158 int); 159 #endif 160 161 static int udl_cmd_send(struct udl_softc *); 162 static void udl_cmd_send_async(struct udl_softc *); 163 static void udl_cmd_send_async_cb(struct usbd_xfer *, 164 void *, usbd_status); 165 166 static int udl_ctrl_msg(struct udl_softc *, uint8_t, uint8_t, 167 uint16_t, uint16_t, uint8_t *, uint16_t); 168 static int udl_init(struct udl_softc *); 169 static void udl_read_edid(struct udl_softc *); 170 static void udl_set_address(struct udl_softc *, int, int, int, 171 int); 172 static void udl_blank(struct udl_softc *, int); 173 static uint16_t udl_lfsr(uint16_t); 174 static int udl_set_resolution(struct udl_softc *, 175 const struct videomode *); 176 static const struct videomode *udl_videomode_lookup(const char *); 177 static void udl_update_thread(void *); 178 static inline void udl_startstop(struct udl_softc *, bool); 179 180 static inline void 181 udl_cmd_add_1(struct udl_softc *sc, uint8_t val) 182 { 183 184 *sc->sc_cmd_buf++ = val; 185 } 186 187 static inline void 188 udl_cmd_add_2(struct udl_softc *sc, uint16_t val) 189 { 190 191 be16enc(sc->sc_cmd_buf, val); 192 sc->sc_cmd_buf += 2; 193 } 194 195 static inline void 196 udl_cmd_add_3(struct udl_softc *sc, uint32_t val) 197 { 198 199 udl_cmd_add_2(sc, val >> 8); 200 udl_cmd_add_1(sc, val); 201 } 202 203 static inline void 204 udl_cmd_add_4(struct udl_softc *sc, uint32_t val) 205 { 206 207 be32enc(sc->sc_cmd_buf, val); 208 sc->sc_cmd_buf += 4; 209 } 210 211 static inline void 212 udl_cmd_add_buf(struct udl_softc *sc, uint16_t *buf, int width) 213 { 214 #if BYTE_ORDER == BIG_ENDIAN 215 memcpy(sc->sc_cmd_buf, buf, width * 2); 216 sc->sc_cmd_buf += width * 2; 217 #else 218 uint16_t *endp; 219 220 endp = buf + width; 221 222 if (((uintptr_t)sc->sc_cmd_buf & 1) == 0) { 223 while (buf < endp) { 224 *(uint16_t *)sc->sc_cmd_buf = htobe16(*buf++); 225 sc->sc_cmd_buf += 2; 226 } 227 } else { 228 while (buf < endp) { 229 be16enc(sc->sc_cmd_buf, *buf++); 230 sc->sc_cmd_buf += 2; 231 } 232 } 233 #endif 234 } 235 236 static inline void 237 udl_reg_write_1(struct udl_softc *sc, uint8_t reg, uint8_t val) 238 { 239 240 udl_cmd_add_4(sc, (UDL_BULK_SOC << 24) | 241 (UDL_BULK_CMD_REG_WRITE_1 << 16) | (reg << 8) | val); 242 } 243 244 static inline void 245 udl_reg_write_2(struct udl_softc *sc, uint8_t reg, uint16_t val) 246 { 247 248 udl_reg_write_1(sc, reg++, val >> 8); 249 udl_reg_write_1(sc, reg, val); 250 } 251 252 static inline void 253 udl_reg_write_3(struct udl_softc *sc, uint8_t reg, uint32_t val) 254 { 255 256 udl_reg_write_1(sc, reg++, val >> 16); 257 udl_reg_write_1(sc, reg++, val >> 8); 258 udl_reg_write_1(sc, reg, val); 259 } 260 261 /* XXX */ 262 static int 263 firmware_load(const char *dname, const char *iname, uint8_t **ucodep, 264 size_t *sizep) 265 { 266 firmware_handle_t fh; 267 int error; 268 269 if ((error = firmware_open(dname, iname, &fh)) != 0) 270 return error; 271 *sizep = firmware_get_size(fh); 272 if ((*ucodep = firmware_malloc(*sizep)) == NULL) { 273 firmware_close(fh); 274 return ENOMEM; 275 } 276 if ((error = firmware_read(fh, 0, *ucodep, *sizep)) != 0) 277 firmware_free(*ucodep, *sizep); 278 firmware_close(fh); 279 280 return error; 281 } 282 283 /* 284 * Driver glue. 285 */ 286 CFATTACH_DECL_NEW(udl, sizeof(struct udl_softc), 287 udl_match, udl_attach, udl_detach, NULL); 288 289 /* 290 * wsdisplay glue. 291 */ 292 static struct wsdisplay_accessops udl_accessops = { 293 udl_ioctl, 294 udl_mmap, 295 udl_alloc_screen, 296 udl_free_screen, 297 udl_show_screen, 298 NULL, 299 NULL, 300 NULL, 301 }; 302 303 /* 304 * Matching devices. 305 */ 306 static const struct usb_devno udl_devs[] = { 307 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020 }, 308 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220 }, 309 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD190 }, 310 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_U70 }, 311 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2 }, 312 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60 }, 313 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV }, 314 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI }, 315 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_USBRGB }, 316 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCDUSB7X }, 317 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCDUSB10X }, 318 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10 }, 319 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI }, 320 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008 }, 321 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GXDVIU2 }, 322 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GXDVIU2B }, 323 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U }, 324 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U }, 325 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK }, 326 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571 }, 327 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061 }, 328 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK }, 329 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI }, 330 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70 }, 331 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000UD_DVI }, 332 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LDEWX015U }, 333 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_MIMO }, 334 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE }, 335 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421WIDE }, 336 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SD_U2VDH }, 337 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0 }, 338 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_FYDVI }, 339 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_FYDVI2 } 340 }; 341 342 static int 343 udl_match(device_t parent, cfdata_t match, void *aux) 344 { 345 struct usb_attach_arg *uaa = aux; 346 347 if (usb_lookup(udl_devs, uaa->uaa_vendor, uaa->uaa_product) != NULL) 348 return UMATCH_VENDOR_PRODUCT; 349 350 return UMATCH_NONE; 351 } 352 353 static void 354 udl_attach(device_t parent, device_t self, void *aux) 355 { 356 struct udl_softc *sc = device_private(self); 357 struct usb_attach_arg *uaa = aux; 358 struct wsemuldisplaydev_attach_args aa; 359 const struct videomode *vmp; 360 usbd_status error; 361 char *devinfop; 362 363 aprint_naive("\n"); 364 aprint_normal("\n"); 365 366 sc->sc_dev = self; 367 sc->sc_udev = uaa->uaa_device; 368 369 devinfop = usbd_devinfo_alloc(sc->sc_udev, 0); 370 aprint_normal_dev(sc->sc_dev, "%s\n", devinfop); 371 usbd_devinfo_free(devinfop); 372 373 /* 374 * Set device configuration descriptor number. 375 */ 376 error = usbd_set_config_no(sc->sc_udev, 1, 0); 377 if (error != USBD_NORMAL_COMPLETION) { 378 aprint_error_dev(self, "failed to set configuration" 379 ", err=%s\n", usbd_errstr(error)); 380 return; 381 } 382 383 /* 384 * Create device handle to interface descriptor. 385 */ 386 error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface); 387 if (error != USBD_NORMAL_COMPLETION) 388 return; 389 390 /* 391 * Open bulk TX pipe. 392 */ 393 error = usbd_open_pipe(sc->sc_iface, 1, USBD_EXCLUSIVE_USE, 394 &sc->sc_tx_pipeh); 395 if (error != USBD_NORMAL_COMPLETION) 396 return; 397 398 /* 399 * Allocate bulk command queue. 400 */ 401 #ifdef UDL_EVENT_COUNTERS 402 evcnt_attach_dynamic(&sc->sc_ev_cmdq_get, EVCNT_TYPE_MISC, NULL, 403 device_xname(sc->sc_dev), "udl_cmdq_get"); 404 evcnt_attach_dynamic(&sc->sc_ev_cmdq_put, EVCNT_TYPE_MISC, NULL, 405 device_xname(sc->sc_dev), "udl_cmdq_put"); 406 evcnt_attach_dynamic(&sc->sc_ev_cmdq_wait, EVCNT_TYPE_MISC, NULL, 407 device_xname(sc->sc_dev), "udl_cmdq_wait"); 408 evcnt_attach_dynamic(&sc->sc_ev_cmdq_timeout, EVCNT_TYPE_MISC, NULL, 409 device_xname(sc->sc_dev), "udl_cmdq_timeout"); 410 #endif 411 412 if (udl_cmdq_alloc(sc) != 0) 413 return; 414 415 cv_init(&sc->sc_cv, device_xname(sc->sc_dev)); 416 mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_TTY); /* XXX for tty_lock */ 417 418 if ((sc->sc_cmd_cur = udl_cmdq_get(sc)) == NULL) 419 return; 420 UDL_CMD_BUFINIT(sc); 421 422 /* 423 * Initialize chip. 424 */ 425 if (udl_init(sc) != 0) 426 return; 427 428 udl_read_edid(sc); 429 430 /* 431 * Initialize resolution. 432 */ 433 #ifndef UDL_VIDEOMODE 434 if (sc->sc_ei.edid_nmodes != 0 && 435 sc->sc_ei.edid_preferred_mode != NULL) 436 vmp = sc->sc_ei.edid_preferred_mode; 437 else 438 #define UDL_VIDEOMODE "640x480x60" 439 #endif 440 vmp = udl_videomode_lookup(UDL_VIDEOMODE); 441 442 if (vmp == NULL) 443 return; 444 445 sc->sc_width = vmp->hdisplay; 446 sc->sc_height = vmp->vdisplay; 447 sc->sc_offscreen = sc->sc_height * 3 / 2; 448 sc->sc_depth = 16; 449 450 if (udl_set_resolution(sc, vmp) != 0) 451 return; 452 453 sc->sc_defaultscreen.name = "default"; 454 sc->sc_screens[0] = &sc->sc_defaultscreen; 455 sc->sc_screenlist.nscreens = 1; 456 sc->sc_screenlist.screens = sc->sc_screens; 457 458 /* 459 * Set initial wsdisplay emulation mode. 460 */ 461 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 462 463 /* 464 * Attach wsdisplay. 465 */ 466 aa.console = 0; 467 aa.scrdata = &sc->sc_screenlist; 468 aa.accessops = &udl_accessops; 469 aa.accesscookie = sc; 470 471 sc->sc_wsdisplay = 472 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint); 473 474 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); 475 476 mutex_init(&sc->sc_thread_mtx, MUTEX_DEFAULT, IPL_NONE); 477 cv_init(&sc->sc_thread_cv, "udlcv"); 478 sc->sc_dying = false; 479 sc->sc_thread_stop = true; 480 kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, 481 udl_update_thread, sc, &sc->sc_thread, "udlupd"); 482 } 483 484 static int 485 udl_detach(device_t self, int flags) 486 { 487 struct udl_softc *sc = device_private(self); 488 489 /* 490 * Close bulk TX pipe. 491 */ 492 if (sc->sc_tx_pipeh != NULL) { 493 usbd_abort_pipe(sc->sc_tx_pipeh); 494 } 495 496 /* 497 * Free command xfer buffers. 498 */ 499 udl_cmdq_flush(sc); 500 udl_cmdq_free(sc); 501 502 if (sc->sc_tx_pipeh != NULL) { 503 usbd_close_pipe(sc->sc_tx_pipeh); 504 } 505 506 /* 507 * Free Huffman table. 508 */ 509 udl_comp_unload(sc); 510 511 /* 512 * Free framebuffer memory. 513 */ 514 udl_fbmem_free(sc); 515 516 mutex_enter(&sc->sc_thread_mtx); 517 sc->sc_dying = true; 518 cv_broadcast(&sc->sc_thread_cv); 519 mutex_exit(&sc->sc_thread_mtx); 520 kthread_join(sc->sc_thread); 521 522 cv_destroy(&sc->sc_cv); 523 mutex_destroy(&sc->sc_mtx); 524 cv_destroy(&sc->sc_thread_cv); 525 mutex_destroy(&sc->sc_thread_mtx); 526 527 /* 528 * Detach wsdisplay. 529 */ 530 if (sc->sc_wsdisplay != NULL) 531 config_detach(sc->sc_wsdisplay, DETACH_FORCE); 532 533 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); 534 535 #ifdef UDL_EVENT_COUNTERS 536 evcnt_detach(&sc->sc_ev_cmdq_get); 537 evcnt_detach(&sc->sc_ev_cmdq_put); 538 evcnt_detach(&sc->sc_ev_cmdq_wait); 539 evcnt_detach(&sc->sc_ev_cmdq_timeout); 540 #endif 541 542 return 0; 543 } 544 545 static int 546 udl_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) 547 { 548 struct udl_softc *sc = v; 549 #ifdef notyet 550 struct udl_ioctl_damage *d; 551 #endif 552 struct wsdisplay_fbinfo *wdf; 553 u_int mode; 554 555 switch (cmd) { 556 case WSDISPLAYIO_GTYPE: 557 *(u_int *)data = WSDISPLAY_TYPE_DL; 558 return 0; 559 560 case WSDISPLAYIO_GINFO: 561 wdf = (struct wsdisplay_fbinfo *)data; 562 wdf->height = sc->sc_height; 563 wdf->width = sc->sc_width; 564 wdf->depth = sc->sc_depth; 565 wdf->cmsize = 0; 566 return 0; 567 568 case WSDISPLAYIO_GVIDEO: 569 *(u_int *)data = sc->sc_blank; 570 return 0; 571 572 case WSDISPLAYIO_SVIDEO: 573 mode = *(u_int *)data; 574 if (mode == sc->sc_blank) 575 return 0; 576 switch (mode) { 577 case WSDISPLAYIO_VIDEO_OFF: 578 udl_startstop(sc, true); 579 udl_blank(sc, 1); 580 break; 581 case WSDISPLAYIO_VIDEO_ON: 582 udl_blank(sc, 0); 583 break; 584 default: 585 return EINVAL; 586 } 587 if (UDL_CMD_BUFSIZE(sc) > 0) 588 udl_cmd_send_async(sc); 589 udl_cmdq_flush(sc); 590 sc->sc_blank = mode; 591 return 0; 592 593 case WSDISPLAYIO_SMODE: 594 mode = *(u_int *)data; 595 if (mode == sc->sc_mode) 596 return 0; 597 switch (mode) { 598 case WSDISPLAYIO_MODE_EMUL: 599 udl_startstop(sc, true); 600 /* clear screen */ 601 udl_fill_rect(sc, 0, 0, 0, sc->sc_width, 602 sc->sc_height); 603 if (UDL_CMD_BUFSIZE(sc) > 0) 604 udl_cmd_send_async(sc); 605 udl_cmdq_flush(sc); 606 udl_comp_unload(sc); 607 break; 608 case WSDISPLAYIO_MODE_DUMBFB: 609 if (UDL_CMD_BUFSIZE(sc) > 0) 610 udl_cmd_send_async(sc); 611 udl_cmdq_flush(sc); 612 udl_comp_load(sc); 613 udl_startstop(sc, false); 614 break; 615 default: 616 return EINVAL; 617 } 618 sc->sc_mode = mode; 619 return 0; 620 621 case WSDISPLAYIO_LINEBYTES: 622 *(u_int *)data = sc->sc_width * (sc->sc_depth / 8); 623 return 0; 624 625 #ifdef notyet 626 /* 627 * XXX 628 * OpenBSD allows device specific ioctl()s and use this 629 * UDLIO_DAMAGE for the damage extension ops of X servers. 630 * Before blindly pulling such interfaces, probably we should 631 * discuss how such devices should be handled which have 632 * in-direct framebuffer memories that should be transfered 633 * per updated rectangle regions via MI wscons APIs. 634 */ 635 case UDLIO_DAMAGE: 636 d = (struct udl_ioctl_damage *)data; 637 d->status = UDLIO_STATUS_OK; 638 if (sc->sc_flags & UDL_COMPRDY) 639 udl_draw_rect_comp(sc, d); 640 else 641 udl_draw_rect(sc, d); 642 return 0; 643 #endif 644 } 645 646 return EPASSTHROUGH; 647 } 648 649 static paddr_t 650 udl_mmap(void *v, void *vs, off_t off, int prot) 651 { 652 struct udl_softc *sc = v; 653 vaddr_t vaddr; 654 paddr_t paddr; 655 bool rv __diagused; 656 657 if (off < 0 || off > roundup2(UDL_FBMEM_SIZE(sc), PAGE_SIZE)) 658 return -1; 659 660 /* allocate framebuffer memory */ 661 if (udl_fbmem_alloc(sc) != 0) 662 return -1; 663 664 udl_startstop(sc, false); 665 666 vaddr = (vaddr_t)sc->sc_fbmem + off; 667 rv = pmap_extract(pmap_kernel(), vaddr, &paddr); 668 KASSERT(rv); 669 paddr += vaddr & PGOFSET; 670 671 /* XXX we need MI paddr_t -> mmap cookie API */ 672 #if defined(__alpha__) 673 #define PTOMMAP(paddr) alpha_btop((char *)paddr) 674 #elif defined(__arm__) 675 #define PTOMMAP(paddr) arm_btop((u_long)paddr) 676 #elif defined(__hppa__) 677 #define PTOMMAP(paddr) btop((u_long)paddr) 678 #elif defined(__i386__) || defined(__x86_64__) 679 #define PTOMMAP(paddr) x86_btop(paddr) 680 #elif defined(__m68k__) 681 #define PTOMMAP(paddr) m68k_btop((char *)paddr) 682 #elif defined(__mips__) 683 #define PTOMMAP(paddr) mips_btop(paddr) 684 #elif defined(__powerpc__) 685 #define PTOMMAP(paddr) (paddr) 686 #elif defined(__sh__) 687 #define PTOMMAP(paddr) sh3_btop(paddr) 688 #elif defined(__sparc__) 689 #define PTOMMAP(paddr) (paddr) 690 #elif defined(__sparc64__) 691 #define PTOMMAP(paddr) atop(paddr) 692 #elif defined(__vax__) 693 #define PTOMMAP(paddr) btop((u_int)paddr) 694 #endif 695 696 return PTOMMAP(paddr); 697 } 698 699 static int 700 udl_alloc_screen(void *v, const struct wsscreen_descr *type, 701 void **cookiep, int *curxp, int *curyp, long *attrp) 702 { 703 struct udl_softc *sc = v; 704 705 if (sc->sc_nscreens > 0) 706 return ENOMEM; 707 708 /* 709 * Initialize rasops. 710 */ 711 sc->sc_ri.ri_depth = sc->sc_depth; 712 sc->sc_ri.ri_bits = NULL; 713 sc->sc_ri.ri_width = sc->sc_width; 714 sc->sc_ri.ri_height = sc->sc_height; 715 sc->sc_ri.ri_stride = sc->sc_width * (sc->sc_depth / 8); 716 sc->sc_ri.ri_hw = sc; 717 sc->sc_ri.ri_flg = 0; 718 719 if (sc->sc_depth == 16) { 720 sc->sc_ri.ri_rnum = 5; 721 sc->sc_ri.ri_gnum = 6; 722 sc->sc_ri.ri_bnum = 5; 723 sc->sc_ri.ri_rpos = 11; 724 sc->sc_ri.ri_gpos = 5; 725 sc->sc_ri.ri_bpos = 0; 726 } 727 728 rasops_init(&sc->sc_ri, sc->sc_height / 8, sc->sc_width / 8); 729 730 sc->sc_ri.ri_ops.cursor = udl_cursor; 731 sc->sc_ri.ri_ops.putchar = udl_putchar; 732 sc->sc_ri.ri_ops.copycols = udl_copycols; 733 sc->sc_ri.ri_ops.erasecols = udl_erasecols; 734 sc->sc_ri.ri_ops.copyrows = udl_copyrows; 735 sc->sc_ri.ri_ops.eraserows = udl_eraserows; 736 737 sc->sc_ri.ri_ops.allocattr(&sc->sc_ri, 0, 0, 0, attrp); 738 739 sc->sc_defaultscreen.ncols = sc->sc_ri.ri_cols; 740 sc->sc_defaultscreen.nrows = sc->sc_ri.ri_rows; 741 sc->sc_defaultscreen.textops = &sc->sc_ri.ri_ops; 742 sc->sc_defaultscreen.fontwidth = sc->sc_ri.ri_font->fontwidth; 743 sc->sc_defaultscreen.fontheight = sc->sc_ri.ri_font->fontheight; 744 sc->sc_defaultscreen.capabilities = sc->sc_ri.ri_caps; 745 746 *cookiep = &sc->sc_ri; 747 *curxp = 0; 748 *curyp = 0; 749 750 sc->sc_nscreens++; 751 752 return 0; 753 } 754 755 static void 756 udl_free_screen(void *v, void *cookie) 757 { 758 struct udl_softc *sc = v; 759 760 sc->sc_nscreens--; 761 } 762 763 static int 764 udl_show_screen(void *v, void *cookie, int waitok, 765 void (*cb)(void *, int, int), void *cbarg) 766 { 767 768 return 0; 769 } 770 771 static inline void 772 udl_cmd_add_decomptable(struct udl_softc *sc, uint8_t *buf, int len) 773 { 774 775 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_DECOMP); 776 udl_cmd_add_4(sc, 0x263871cd); /* magic number */ 777 udl_cmd_add_4(sc, 0x00000200); /* 512 byte chunks */ 778 memcpy(sc->sc_cmd_buf, buf, len); 779 sc->sc_cmd_buf += len; 780 } 781 782 static void 783 udl_comp_load(struct udl_softc *sc) 784 { 785 struct udl_huffman *h; 786 uint8_t *decomp; 787 size_t decomp_size; 788 int error, i; 789 790 if (!(sc->sc_flags & UDL_DECOMPRDY)) { 791 error = firmware_load("udl", "udl-decomp", &decomp, 792 &decomp_size); 793 if (error != 0) { 794 aprint_error_dev(sc->sc_dev, 795 "error %d, could not read decomp table %s!\n", 796 error, "udl-decomp"); 797 return; 798 } 799 udl_cmd_add_decomptable(sc, decomp, decomp_size); 800 firmware_free(decomp, decomp_size); 801 if (udl_cmd_send(sc) != 0) 802 return; 803 sc->sc_flags |= UDL_DECOMPRDY; 804 } 805 806 if (!(sc->sc_flags & UDL_COMPRDY)) { 807 error = firmware_load("udl", "udl-comp", &sc->sc_huffman, 808 &sc->sc_huffman_size); 809 if (error != 0) { 810 aprint_error_dev(sc->sc_dev, 811 "error %d, could not read huffman table %s!\n", 812 error, "udl-comp"); 813 return; 814 } 815 h = (struct udl_huffman *)sc->sc_huffman; 816 for (i = 0; i < UDL_HUFFMAN_RECORDS; i++) 817 h[i].bit_pattern = be32toh(h[i].bit_pattern); 818 sc->sc_huffman_base = sc->sc_huffman + UDL_HUFFMAN_BASE; 819 sc->sc_flags |= UDL_COMPRDY; 820 } 821 } 822 823 static void 824 udl_comp_unload(struct udl_softc *sc) 825 { 826 827 if (sc->sc_flags & UDL_COMPRDY) { 828 firmware_free(sc->sc_huffman, sc->sc_huffman_size); 829 sc->sc_huffman = NULL; 830 sc->sc_huffman_size = 0; 831 sc->sc_flags &= ~UDL_COMPRDY; 832 } 833 } 834 835 static int 836 udl_fbmem_alloc(struct udl_softc *sc) 837 { 838 839 mutex_enter(&sc->sc_thread_mtx); 840 if (sc->sc_fbmem == NULL) { 841 sc->sc_fbmem = kmem_zalloc(UDL_FBMEM_SIZE(sc), KM_SLEEP); 842 if (sc->sc_fbmem == NULL) { 843 mutex_exit(&sc->sc_thread_mtx); 844 return -1; 845 } 846 } 847 if (sc->sc_fbmem_prev == NULL) { 848 sc->sc_fbmem_prev = kmem_zalloc(UDL_FBMEM_SIZE(sc), KM_SLEEP); 849 if (sc->sc_fbmem_prev == NULL) { 850 kmem_free(sc->sc_fbmem, UDL_FBMEM_SIZE(sc)); 851 sc->sc_fbmem = NULL; 852 mutex_exit(&sc->sc_thread_mtx); 853 return -1; 854 } 855 } 856 mutex_exit(&sc->sc_thread_mtx); 857 858 return 0; 859 } 860 861 static void 862 udl_fbmem_free(struct udl_softc *sc) 863 { 864 865 mutex_enter(&sc->sc_thread_mtx); 866 if (sc->sc_fbmem != NULL) { 867 kmem_free(sc->sc_fbmem, UDL_FBMEM_SIZE(sc)); 868 sc->sc_fbmem = NULL; 869 } 870 if (sc->sc_fbmem_prev != NULL) { 871 kmem_free(sc->sc_fbmem_prev, UDL_FBMEM_SIZE(sc)); 872 sc->sc_fbmem_prev = NULL; 873 } 874 mutex_exit(&sc->sc_thread_mtx); 875 } 876 877 static int 878 udl_cmdq_alloc(struct udl_softc *sc) 879 { 880 struct udl_cmdq *cmdq; 881 int i; 882 883 TAILQ_INIT(&sc->sc_freecmd); 884 TAILQ_INIT(&sc->sc_xfercmd); 885 886 for (i = 0; i < UDL_NCMDQ; i++) { 887 cmdq = &sc->sc_cmdq[i]; 888 889 cmdq->cq_sc = sc; 890 891 int err = usbd_create_xfer(sc->sc_tx_pipeh, 892 UDL_CMD_BUFFER_SIZE, 0, 0, &cmdq->cq_xfer); 893 if (err) { 894 aprint_error_dev(sc->sc_dev, 895 "%s: can't allocate xfer handle!\n", __func__); 896 goto error; 897 } 898 899 cmdq->cq_buf = usbd_get_buffer(cmdq->cq_xfer); 900 901 TAILQ_INSERT_TAIL(&sc->sc_freecmd, cmdq, cq_chain); 902 } 903 904 return 0; 905 906 error: 907 udl_cmdq_free(sc); 908 return -1; 909 } 910 911 static void 912 udl_cmdq_free(struct udl_softc *sc) 913 { 914 struct udl_cmdq *cmdq; 915 int i; 916 917 for (i = 0; i < UDL_NCMDQ; i++) { 918 cmdq = &sc->sc_cmdq[i]; 919 920 if (cmdq->cq_xfer != NULL) { 921 usbd_destroy_xfer(cmdq->cq_xfer); 922 cmdq->cq_xfer = NULL; 923 cmdq->cq_buf = NULL; 924 } 925 } 926 } 927 928 static struct udl_cmdq * 929 udl_cmdq_get(struct udl_softc *sc) 930 { 931 struct udl_cmdq *cmdq; 932 933 cmdq = TAILQ_FIRST(&sc->sc_freecmd); 934 if (cmdq != NULL) { 935 TAILQ_REMOVE(&sc->sc_freecmd, cmdq, cq_chain); 936 UDL_EVCNT_INCR(&sc->sc_ev_cmdq_get); 937 } 938 939 return cmdq; 940 } 941 942 static void 943 udl_cmdq_put(struct udl_softc *sc, struct udl_cmdq *cmdq) 944 { 945 946 TAILQ_INSERT_TAIL(&sc->sc_freecmd, cmdq, cq_chain); 947 UDL_EVCNT_INCR(&sc->sc_ev_cmdq_put); 948 } 949 950 static void 951 udl_cmdq_flush(struct udl_softc *sc) 952 { 953 954 mutex_enter(&sc->sc_mtx); 955 while (TAILQ_FIRST(&sc->sc_xfercmd) != NULL) 956 cv_wait(&sc->sc_cv, &sc->sc_mtx); 957 mutex_exit(&sc->sc_mtx); 958 } 959 960 static void 961 udl_cursor(void *cookie, int on, int row, int col) 962 { 963 struct rasops_info *ri = cookie; 964 struct udl_softc *sc = ri->ri_hw; 965 int x, y, width, height; 966 967 if (ri->ri_flg & RI_CURSOR) 968 udl_restore_char(ri); 969 970 ri->ri_crow = row; 971 ri->ri_ccol = col; 972 973 if (on != 0) { 974 ri->ri_flg |= RI_CURSOR; 975 976 x = col * ri->ri_font->fontwidth; 977 y = row * ri->ri_font->fontheight; 978 width = ri->ri_font->fontwidth; 979 height = ri->ri_font->fontheight; 980 981 /* save the last character block to off-screen */ 982 udl_copy_rect(sc, x, y, 0, sc->sc_offscreen, width, height); 983 984 /* draw cursor */ 985 udl_fill_rect(sc, 0xffff, x, y, width, 1); 986 udl_fill_rect(sc, 0xffff, x, y + 1, 1, height - 2); 987 udl_fill_rect(sc, 0xffff, x + width - 1, y + 1, 1, height - 2); 988 udl_fill_rect(sc, 0xffff, x, y + height - 1, width, 1); 989 990 udl_cmd_send_async(sc); 991 } else 992 ri->ri_flg &= ~RI_CURSOR; 993 } 994 995 static void 996 udl_putchar(void *cookie, int row, int col, u_int uc, long attr) 997 { 998 struct rasops_info *ri = cookie; 999 struct udl_softc *sc = ri->ri_hw; 1000 uint16_t rgb16[2]; 1001 int fg, bg, underline, x, y, width, height; 1002 1003 rasops_unpack_attr(attr, &fg, &bg, &underline); 1004 rgb16[1] = (uint16_t)ri->ri_devcmap[fg]; 1005 rgb16[0] = (uint16_t)ri->ri_devcmap[bg]; 1006 1007 x = col * ri->ri_font->fontwidth; 1008 y = row * ri->ri_font->fontheight; 1009 width = ri->ri_font->fontwidth; 1010 height = ri->ri_font->fontheight; 1011 1012 if (uc == ' ') { 1013 /* 1014 * Writting a block for the space character instead rendering 1015 * it from font bits is more slim. 1016 */ 1017 udl_fill_rect(sc, rgb16[0], x, y, width, height); 1018 } else { 1019 /* render a character from font bits */ 1020 udl_draw_char(ri, rgb16, uc, x, y); 1021 } 1022 1023 if (underline != 0) 1024 udl_fill_rect(sc, rgb16[1], x, y + height - 1, width, 1); 1025 1026 #if 0 1027 udl_cmd_send_async(sc); 1028 #endif 1029 } 1030 1031 static void 1032 udl_copycols(void *cookie, int row, int src, int dst, int num) 1033 { 1034 struct rasops_info *ri = cookie; 1035 struct udl_softc *sc = ri->ri_hw; 1036 int sx, dx, y, width, height; 1037 1038 sx = src * ri->ri_font->fontwidth; 1039 dx = dst * ri->ri_font->fontwidth; 1040 y = row * ri->ri_font->fontheight; 1041 width = num * ri->ri_font->fontwidth; 1042 height = ri->ri_font->fontheight; 1043 1044 /* copy row block to off-screen first to fix overlay-copy problem */ 1045 udl_copy_rect(sc, sx, y, 0, sc->sc_offscreen, width, height); 1046 1047 /* copy row block back from off-screen now */ 1048 udl_copy_rect(sc, 0, sc->sc_offscreen, dx, y, width, height); 1049 #if 0 1050 udl_cmd_send_async(sc); 1051 #endif 1052 } 1053 1054 static void 1055 udl_erasecols(void *cookie, int row, int col, int num, long attr) 1056 { 1057 struct rasops_info *ri = cookie; 1058 struct udl_softc *sc = ri->ri_hw; 1059 uint16_t rgb16; 1060 int fg, bg, x, y, width, height; 1061 1062 rasops_unpack_attr(attr, &fg, &bg, NULL); 1063 rgb16 = (uint16_t)ri->ri_devcmap[bg]; 1064 1065 x = col * ri->ri_font->fontwidth; 1066 y = row * ri->ri_font->fontheight; 1067 width = num * ri->ri_font->fontwidth; 1068 height = ri->ri_font->fontheight; 1069 1070 udl_fill_rect(sc, rgb16, x, y, width, height); 1071 #if 0 1072 udl_cmd_send_async(sc); 1073 #endif 1074 } 1075 1076 static void 1077 udl_copyrows(void *cookie, int src, int dst, int num) 1078 { 1079 struct rasops_info *ri = cookie; 1080 struct udl_softc *sc = ri->ri_hw; 1081 int sy, ey, dy, width, height; 1082 1083 width = ri->ri_emuwidth; 1084 height = ri->ri_font->fontheight; 1085 1086 if (dst < src) { 1087 sy = src * height; 1088 ey = (src + num) * height; 1089 dy = dst * height; 1090 1091 while (sy < ey) { 1092 udl_copy_rect(sc, 0, sy, 0, dy, width, height); 1093 sy += height; 1094 dy += height; 1095 } 1096 } else { 1097 sy = (src + num) * height; 1098 ey = src * height; 1099 dy = (dst + num) * height; 1100 1101 while (sy > ey) { 1102 sy -= height; 1103 dy -= height; 1104 udl_copy_rect(sc, 0, sy, 0, dy, width, height); 1105 } 1106 } 1107 #if 0 1108 udl_cmd_send_async(sc); 1109 #endif 1110 } 1111 1112 static void 1113 udl_eraserows(void *cookie, int row, int num, long attr) 1114 { 1115 struct rasops_info *ri = cookie; 1116 struct udl_softc *sc = ri->ri_hw; 1117 uint16_t rgb16; 1118 int fg, bg, y, width, height; 1119 1120 rasops_unpack_attr(attr, &fg, &bg, NULL); 1121 rgb16 = (uint16_t)ri->ri_devcmap[bg]; 1122 1123 y = row * ri->ri_font->fontheight; 1124 width = ri->ri_emuwidth; 1125 height = num * ri->ri_font->fontheight; 1126 1127 udl_fill_rect(sc, rgb16, 0, y, width, height); 1128 #if 0 1129 udl_cmd_send_async(sc); 1130 #endif 1131 } 1132 1133 static void 1134 udl_restore_char(struct rasops_info *ri) 1135 { 1136 struct udl_softc *sc = ri->ri_hw; 1137 int x, y, width, height; 1138 1139 x = ri->ri_ccol * ri->ri_font->fontwidth; 1140 y = ri->ri_crow * ri->ri_font->fontheight; 1141 width = ri->ri_font->fontwidth; 1142 height = ri->ri_font->fontheight; 1143 1144 /* restore the last saved character from off-screen */ 1145 udl_copy_rect(sc, 0, sc->sc_offscreen, x, y, width, height); 1146 } 1147 1148 static void 1149 udl_draw_char(struct rasops_info *ri, uint16_t *rgb16, u_int uc, int x, int y) 1150 { 1151 struct udl_softc *sc = ri->ri_hw; 1152 struct wsdisplay_font *font = ri->ri_font; 1153 uint32_t fontbits; 1154 uint16_t pixels[32]; 1155 uint8_t *fontbase; 1156 int i, soff, eoff; 1157 1158 soff = y * sc->sc_width + x; 1159 eoff = (y + font->fontheight) * sc->sc_width + x; 1160 fontbase = (uint8_t *)font->data + (uc - font->firstchar) * 1161 ri->ri_fontscale; 1162 1163 while (soff < eoff) { 1164 fontbits = 0; 1165 switch (font->stride) { 1166 case 4: 1167 fontbits |= fontbase[3]; 1168 /* FALLTHROUGH */ 1169 case 3: 1170 fontbits |= fontbase[2] << 8; 1171 /* FALLTHROUGH */ 1172 case 2: 1173 fontbits |= fontbase[1] << 16; 1174 /* FALLTHROUGH */ 1175 case 1: 1176 fontbits |= fontbase[0] << 24; 1177 } 1178 fontbase += font->stride; 1179 1180 for (i = 0; i < font->fontwidth; i++) { 1181 pixels[i] = rgb16[(fontbits >> 31) & 1]; 1182 fontbits <<= 1; 1183 } 1184 1185 udl_draw_line(sc, pixels, soff, font->fontwidth); 1186 soff += sc->sc_width; 1187 } 1188 } 1189 1190 static void 1191 udl_copy_rect(struct udl_softc *sc, int sx, int sy, int dx, int dy, int width, 1192 int height) 1193 { 1194 int sbase, soff, ebase, eoff, dbase, doff, width_cur; 1195 1196 sbase = sy * sc->sc_width; 1197 ebase = (sy + height) * sc->sc_width; 1198 dbase = dy * sc->sc_width; 1199 1200 while (width > 0) { 1201 soff = sbase + sx; 1202 eoff = ebase + sx; 1203 doff = dbase + dx; 1204 1205 if (width >= UDL_CMD_WIDTH_MAX) 1206 width_cur = UDL_CMD_WIDTH_MAX; 1207 else 1208 width_cur = width; 1209 1210 while (soff < eoff) { 1211 udl_copy_line(sc, soff, doff, width_cur); 1212 soff += sc->sc_width; 1213 doff += sc->sc_width; 1214 } 1215 1216 sx += width_cur; 1217 dx += width_cur; 1218 width -= width_cur; 1219 } 1220 } 1221 1222 static void 1223 udl_fill_rect(struct udl_softc *sc, uint16_t rgb16, int x, int y, int width, 1224 int height) 1225 { 1226 int sbase, soff, ebase, eoff, width_cur; 1227 1228 sbase = y * sc->sc_width; 1229 ebase = (y + height) * sc->sc_width; 1230 1231 while (width > 0) { 1232 soff = sbase + x; 1233 eoff = ebase + x; 1234 1235 if (width >= UDL_CMD_WIDTH_MAX) 1236 width_cur = UDL_CMD_WIDTH_MAX; 1237 else 1238 width_cur = width; 1239 1240 while (soff < eoff) { 1241 udl_fill_line(sc, rgb16, soff, width_cur); 1242 soff += sc->sc_width; 1243 } 1244 1245 x += width_cur; 1246 width -= width_cur; 1247 } 1248 } 1249 1250 #ifdef notyet 1251 static void 1252 udl_draw_rect(struct udl_softc *sc, struct udl_ioctl_damage *d) 1253 { 1254 int sbase, soff, ebase, eoff, x, y, width, width_cur, height; 1255 1256 x = d->x1; 1257 y = d->y1; 1258 width = d->x2 - d->x1; 1259 height = d->y2 - d->y1; 1260 sbase = y * sc->sc_width; 1261 ebase = (y + height) * sc->sc_width; 1262 1263 while (width > 0) { 1264 soff = sbase + x; 1265 eoff = ebase + x; 1266 1267 if (width >= UDL_CMD_WIDTH_MAX) 1268 width_cur = UDL_CMD_WIDTH_MAX; 1269 else 1270 width_cur = width; 1271 1272 while (soff < eoff) { 1273 udl_draw_line(sc, (uint16_t *)sc->sc_fbmem + soff, 1274 soff, width_cur); 1275 soff += sc->sc_width; 1276 } 1277 1278 x += width_cur; 1279 width -= width_cur; 1280 } 1281 1282 udl_cmd_send_async(sc); 1283 } 1284 1285 static void 1286 udl_draw_rect_comp(struct udl_softc *sc, struct udl_ioctl_damage *d) 1287 { 1288 int soff, eoff, x, y, width, height; 1289 1290 x = d->x1; 1291 y = d->y1; 1292 width = d->x2 - d->x1; 1293 height = d->y2 - d->y1; 1294 soff = y * sc->sc_width + x; 1295 eoff = (y + height) * sc->sc_width + x; 1296 1297 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 1298 sc->sc_cmd_cblen = 4; 1299 1300 while (soff < eoff) { 1301 udl_draw_line_comp(sc, (uint16_t *)sc->sc_fbmem + soff, soff, 1302 width); 1303 soff += sc->sc_width; 1304 } 1305 1306 udl_cmd_send_async(sc); 1307 } 1308 #endif 1309 1310 static inline void 1311 udl_copy_line(struct udl_softc *sc, int soff, int doff, int width) 1312 { 1313 1314 if (__predict_false((UDL_CMD_BUFSIZE(sc) + UDL_CMD_COPY_SIZE + 2) > 1315 UDL_CMD_BUFFER_SIZE)) 1316 udl_cmd_send_async(sc); 1317 1318 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_COPY16); 1319 udl_cmd_add_4(sc, ((doff * 2) << 8) | (width & 0xff)); 1320 1321 udl_cmd_add_3(sc, soff * 2); 1322 } 1323 1324 static inline void 1325 udl_fill_line(struct udl_softc *sc, uint16_t rgb16, int off, int width) 1326 { 1327 1328 if (__predict_false((UDL_CMD_BUFSIZE(sc) + UDL_CMD_FILL_SIZE + 2) > 1329 UDL_CMD_BUFFER_SIZE)) 1330 udl_cmd_send_async(sc); 1331 1332 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_RLE16); 1333 udl_cmd_add_4(sc, ((off * 2) << 8) | (width & 0xff)); 1334 1335 udl_cmd_add_1(sc, width); 1336 udl_cmd_add_2(sc, rgb16); 1337 } 1338 1339 static inline void 1340 udl_draw_line(struct udl_softc *sc, uint16_t *buf, int off, int width) 1341 { 1342 1343 if (__predict_false( 1344 (UDL_CMD_BUFSIZE(sc) + UDL_CMD_DRAW_SIZE(width) + 2) > 1345 UDL_CMD_BUFFER_SIZE)) 1346 udl_cmd_send_async(sc); 1347 1348 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_WRITE16); 1349 udl_cmd_add_4(sc, ((off * 2) << 8) | (width & 0xff)); 1350 1351 udl_cmd_add_buf(sc, buf, width); 1352 } 1353 1354 #ifdef notyet 1355 static inline int 1356 udl_cmd_add_buf_comp(struct udl_softc *sc, uint16_t *buf, int width) 1357 { 1358 struct udl_huffman *h; 1359 uint16_t *startp, *endp; 1360 uint32_t bit_pattern; 1361 uint16_t prev; 1362 int16_t diff; 1363 uint8_t bit_count, bit_pos, bit_rem, curlen; 1364 1365 startp = buf; 1366 if (width >= UDL_CMD_WIDTH_MAX) 1367 endp = buf + UDL_CMD_WIDTH_MAX; 1368 else 1369 endp = buf + width; 1370 1371 prev = bit_pos = *sc->sc_cmd_buf = 0; 1372 bit_rem = 8; 1373 1374 /* 1375 * Generate a sub-block with maximal 256 pixels compressed data. 1376 */ 1377 while (buf < endp) { 1378 /* get difference between current and previous pixel */ 1379 diff = *buf - prev; 1380 1381 /* get the huffman difference bit sequence */ 1382 h = (struct udl_huffman *)sc->sc_huffman_base + diff; 1383 bit_count = h->bit_count; 1384 bit_pattern = h->bit_pattern; 1385 1386 curlen = (bit_pos + bit_count + 7) / 8; 1387 if (__predict_false((sc->sc_cmd_cblen + curlen + 1) > 1388 UDL_CMD_COMP_BLOCK_SIZE)) 1389 break; 1390 1391 /* generate one pixel compressed data */ 1392 while (bit_count >= bit_rem) { 1393 *sc->sc_cmd_buf++ |= 1394 (bit_pattern & ((1 << bit_rem) - 1)) << bit_pos; 1395 *sc->sc_cmd_buf = 0; 1396 sc->sc_cmd_cblen++; 1397 bit_count -= bit_rem; 1398 bit_pattern >>= bit_rem; 1399 bit_pos = 0; 1400 bit_rem = 8; 1401 } 1402 1403 if (bit_count > 0) { 1404 *sc->sc_cmd_buf |= 1405 (bit_pattern & ((1 << bit_count) - 1)) << bit_pos; 1406 bit_pos += bit_count; 1407 bit_rem -= bit_count; 1408 } 1409 1410 prev = *buf++; 1411 } 1412 1413 /* 1414 * If we have bits left in our last byte, round up to the next 1415 * byte, so we don't overwrite them. 1416 */ 1417 if (bit_pos > 0) { 1418 sc->sc_cmd_buf++; 1419 sc->sc_cmd_cblen++; 1420 } 1421 1422 /* return how many pixels we have compressed */ 1423 return buf - startp; 1424 } 1425 1426 static inline void 1427 udl_draw_line_comp(struct udl_softc *sc, uint16_t *buf, int off, int width) 1428 { 1429 uint8_t *widthp; 1430 int width_cur; 1431 1432 while (width > 0) { 1433 if (__predict_false( 1434 (sc->sc_cmd_cblen + UDL_CMD_COMP_MIN_SIZE + 1) > 1435 UDL_CMD_COMP_BLOCK_SIZE)) { 1436 if (UDL_CMD_BUFSIZE(sc) < UDL_CMD_COMP_THRESHOLD) { 1437 while (sc->sc_cmd_cblen < 1438 UDL_CMD_COMP_BLOCK_SIZE) { 1439 *sc->sc_cmd_buf++ = 0; 1440 sc->sc_cmd_cblen++; 1441 } 1442 } else 1443 udl_cmd_send_async(sc); 1444 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 1445 sc->sc_cmd_cblen = 4; 1446 } 1447 1448 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | 1449 (UDL_BULK_CMD_FB_WRITE16 | UDL_BULK_CMD_FB_COMP)); 1450 udl_cmd_add_4(sc, (off * 2) << 8); 1451 1452 widthp = sc->sc_cmd_buf - 1; 1453 1454 sc->sc_cmd_cblen += UDL_CMD_HEADER_SIZE; 1455 1456 width_cur = udl_cmd_add_buf_comp(sc, buf, width); 1457 1458 *widthp = width_cur; 1459 buf += width_cur; 1460 off += width_cur; 1461 width -= width_cur; 1462 } 1463 } 1464 #endif 1465 1466 static int 1467 udl_cmd_send(struct udl_softc *sc) 1468 { 1469 struct udl_cmdq *cmdq; 1470 usbd_status error; 1471 uint32_t len; 1472 1473 cmdq = sc->sc_cmd_cur; 1474 1475 /* mark end of command stack */ 1476 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_EOC); 1477 1478 len = UDL_CMD_BUFSIZE(sc); 1479 1480 /* do xfer */ 1481 error = usbd_bulk_transfer(cmdq->cq_xfer, sc->sc_tx_pipeh, 0, 1482 USBD_NO_TIMEOUT, cmdq->cq_buf, &len); 1483 1484 UDL_CMD_BUFINIT(sc); 1485 1486 if (error != USBD_NORMAL_COMPLETION) { 1487 aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__, 1488 usbd_errstr(error)); 1489 return -1; 1490 } 1491 1492 return 0; 1493 } 1494 1495 static void 1496 udl_cmd_send_async(struct udl_softc *sc) 1497 { 1498 struct udl_cmdq *cmdq; 1499 usbd_status error; 1500 uint32_t len; 1501 1502 #if 1 1503 /* 1504 * XXX 1505 * All tty ops for wsemul are called with tty_lock spin mutex held, 1506 * so we can't call cv_wait(9) here to acquire a free buffer. 1507 * For now, all commands and data for wsemul ops are discarded 1508 * if there is no free command buffer, and then screen text might 1509 * be corrupted on large scroll ops etc. 1510 * 1511 * Probably we have to reorganize the giant tty_lock mutex, or 1512 * change wsdisplay APIs (especially wsdisplaystart()) to return 1513 * a number of actually handled characters as OpenBSD does, but 1514 * the latter one requires whole API changes around rasops(9) etc. 1515 */ 1516 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) { 1517 if (TAILQ_FIRST(&sc->sc_freecmd) == NULL) { 1518 UDL_CMD_BUFINIT(sc); 1519 return; 1520 } 1521 } 1522 #endif 1523 1524 cmdq = sc->sc_cmd_cur; 1525 1526 /* mark end of command stack */ 1527 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_EOC); 1528 1529 len = UDL_CMD_BUFSIZE(sc); 1530 1531 /* do xfer */ 1532 mutex_enter(&sc->sc_mtx); 1533 usbd_setup_xfer(cmdq->cq_xfer, cmdq, cmdq->cq_buf, 1534 len, 0, USBD_NO_TIMEOUT, udl_cmd_send_async_cb); 1535 error = usbd_transfer(cmdq->cq_xfer); 1536 if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { 1537 aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__, 1538 usbd_errstr(error)); 1539 mutex_exit(&sc->sc_mtx); 1540 goto end; 1541 } 1542 1543 TAILQ_INSERT_TAIL(&sc->sc_xfercmd, cmdq, cq_chain); 1544 cmdq = udl_cmdq_get(sc); 1545 mutex_exit(&sc->sc_mtx); 1546 while (cmdq == NULL) { 1547 int err; 1548 UDL_EVCNT_INCR(&sc->sc_ev_cmdq_wait); 1549 mutex_enter(&sc->sc_mtx); 1550 err = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, 1551 mstohz(100) /* XXX is this needed? */); 1552 if (err != 0) { 1553 DPRINTF(("%s: %s: cv timeout (error = %d)\n", 1554 device_xname(sc->sc_dev), __func__, err)); 1555 UDL_EVCNT_INCR(&sc->sc_ev_cmdq_timeout); 1556 } 1557 cmdq = udl_cmdq_get(sc); 1558 mutex_exit(&sc->sc_mtx); 1559 } 1560 sc->sc_cmd_cur = cmdq; 1561 end: 1562 UDL_CMD_BUFINIT(sc); 1563 } 1564 1565 static void 1566 udl_cmd_send_async_cb(struct usbd_xfer *xfer, void * priv, 1567 usbd_status status) 1568 { 1569 struct udl_cmdq *cmdq = priv; 1570 struct udl_softc *sc = cmdq->cq_sc; 1571 1572 if (status != USBD_NORMAL_COMPLETION) { 1573 aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__, 1574 usbd_errstr(status)); 1575 1576 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) 1577 return; 1578 if (status == USBD_STALLED) 1579 usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh); 1580 } 1581 1582 mutex_enter(&sc->sc_mtx); 1583 TAILQ_REMOVE(&sc->sc_xfercmd, cmdq, cq_chain); 1584 udl_cmdq_put(sc, cmdq); 1585 1586 /* signal xfer op that sleeps for a free xfer buffer */ 1587 cv_signal(&sc->sc_cv); 1588 mutex_exit(&sc->sc_mtx); 1589 } 1590 1591 static int 1592 udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r, uint16_t index, 1593 uint16_t value, uint8_t *buf, uint16_t len) 1594 { 1595 usb_device_request_t req; 1596 usbd_status error; 1597 1598 req.bmRequestType = rt; 1599 req.bRequest = r; 1600 USETW(req.wIndex, index); 1601 USETW(req.wValue, value); 1602 USETW(req.wLength, len); 1603 1604 error = usbd_do_request(sc->sc_udev, &req, buf); 1605 if (error != USBD_NORMAL_COMPLETION) { 1606 aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__, 1607 usbd_errstr(error)); 1608 return -1; 1609 } 1610 1611 return 0; 1612 } 1613 1614 static int 1615 udl_init(struct udl_softc *sc) 1616 { 1617 static uint8_t key[16] = { 1618 0x57, 0xcd, 0xdc, 0xa7, 0x1c, 0x88, 0x5e, 0x15, 1619 0x60, 0xfe, 0xc6, 0x97, 0x16, 0x3d, 0x47, 0xf2 1620 }; 1621 uint8_t status[4], val; 1622 1623 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_STATUS, 1624 0x0000, 0x0000, status, sizeof(status)) != 0) 1625 return -1; 1626 1627 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_1, 1628 0xc484, 0x0000, &val, 1) != 0) 1629 return -1; 1630 1631 val = 1; 1632 if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_WRITE_1, 1633 0xc41f, 0x0000, &val, 1) != 0) 1634 return -1; 1635 1636 if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_SET_KEY, 1637 0x0000, 0x0000, key, sizeof(key)) != 0) 1638 return -1; 1639 1640 val = 0; 1641 if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_WRITE_1, 1642 0xc40b, 0x0000, &val, 1) != 0) 1643 return -1; 1644 1645 return 0; 1646 } 1647 1648 static void 1649 udl_read_edid(struct udl_softc *sc) 1650 { 1651 uint8_t buf[64], edid[128]; 1652 int offset; 1653 1654 memset(&sc->sc_ei, 0, sizeof(struct edid_info)); 1655 1656 offset = 0; 1657 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID, 1658 0x00a1, (offset << 8), buf, 64) != 0) 1659 return; 1660 if (buf[0] != 0) 1661 return; 1662 memcpy(&edid[offset], &buf[1], 63); 1663 offset += 63; 1664 1665 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID, 1666 0x00a1, (offset << 8), buf, 64) != 0) 1667 return; 1668 if (buf[0] != 0) 1669 return; 1670 memcpy(&edid[offset], &buf[1], 63); 1671 offset += 63; 1672 1673 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID, 1674 0x00a1, (offset << 8), buf, 3) != 0) 1675 return; 1676 if (buf[0] != 0) 1677 return; 1678 memcpy(&edid[offset], &buf[1], 2); 1679 1680 if (edid_parse(edid, &sc->sc_ei) == 0) { 1681 #ifdef UDL_DEBUG 1682 edid_print(&sc->sc_ei); 1683 #endif 1684 } 1685 } 1686 1687 static void 1688 udl_set_address(struct udl_softc *sc, int start16, int stride16, int start8, 1689 int stride8) 1690 { 1691 udl_reg_write_1(sc, UDL_REG_SYNC, 0x00); 1692 udl_reg_write_3(sc, UDL_REG_ADDR_START16, start16); 1693 udl_reg_write_3(sc, UDL_REG_ADDR_STRIDE16, stride16); 1694 udl_reg_write_3(sc, UDL_REG_ADDR_START8, start8); 1695 udl_reg_write_3(sc, UDL_REG_ADDR_STRIDE8, stride8); 1696 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 1697 } 1698 1699 static void 1700 udl_blank(struct udl_softc *sc, int blank) 1701 { 1702 1703 if (blank != 0) 1704 udl_reg_write_1(sc, UDL_REG_BLANK, UDL_REG_BLANK_ON); 1705 else 1706 udl_reg_write_1(sc, UDL_REG_BLANK, UDL_REG_BLANK_OFF); 1707 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 1708 } 1709 1710 static uint16_t 1711 udl_lfsr(uint16_t count) 1712 { 1713 uint16_t val = 0xffff; 1714 1715 while (count > 0) { 1716 val = (uint16_t)(val << 1) | ((uint16_t)( 1717 (uint16_t)(val << 0) ^ 1718 (uint16_t)(val << 11) ^ 1719 (uint16_t)(val << 13) ^ 1720 (uint16_t)(val << 14) 1721 ) >> 15); 1722 count--; 1723 } 1724 1725 return val; 1726 } 1727 1728 static int 1729 udl_set_resolution(struct udl_softc *sc, const struct videomode *vmp) 1730 { 1731 uint16_t val; 1732 int start16, stride16, start8, stride8; 1733 1734 /* set video memory offsets */ 1735 start16 = 0; 1736 stride16 = sc->sc_width * 2; 1737 start8 = stride16 * sc->sc_height; 1738 stride8 = sc->sc_width; 1739 udl_set_address(sc, start16, stride16, start8, stride8); 1740 1741 /* write resolution values */ 1742 udl_reg_write_1(sc, UDL_REG_SYNC, 0x00); 1743 udl_reg_write_1(sc, UDL_REG_COLORDEPTH, UDL_REG_COLORDEPTH_16); 1744 val = vmp->htotal - vmp->hsync_start; 1745 udl_reg_write_2(sc, UDL_REG_XDISPLAYSTART, udl_lfsr(val)); 1746 val += vmp->hdisplay; 1747 udl_reg_write_2(sc, UDL_REG_XDISPLAYEND, udl_lfsr(val)); 1748 val = vmp->vtotal - vmp->vsync_start; 1749 udl_reg_write_2(sc, UDL_REG_YDISPLAYSTART, udl_lfsr(val)); 1750 val += vmp->vdisplay; 1751 udl_reg_write_2(sc, UDL_REG_YDISPLAYEND, udl_lfsr(val)); 1752 val = vmp->htotal - 1; 1753 udl_reg_write_2(sc, UDL_REG_XENDCOUNT, udl_lfsr(val)); 1754 val = vmp->hsync_end - vmp->hsync_start + 1; 1755 if (vmp->flags & VID_PHSYNC) { 1756 udl_reg_write_2(sc, UDL_REG_HSYNCSTART, udl_lfsr(1)); 1757 udl_reg_write_2(sc, UDL_REG_HSYNCEND, udl_lfsr(val)); 1758 } else { 1759 udl_reg_write_2(sc, UDL_REG_HSYNCSTART, udl_lfsr(val)); 1760 udl_reg_write_2(sc, UDL_REG_HSYNCEND, udl_lfsr(1)); 1761 } 1762 val = vmp->hdisplay; 1763 udl_reg_write_2(sc, UDL_REG_HPIXELS, val); 1764 val = vmp->vtotal; 1765 udl_reg_write_2(sc, UDL_REG_YENDCOUNT, udl_lfsr(val)); 1766 val = vmp->vsync_end - vmp->vsync_start; 1767 if (vmp->flags & VID_PVSYNC) { 1768 udl_reg_write_2(sc, UDL_REG_VSYNCSTART, udl_lfsr(0)); 1769 udl_reg_write_2(sc, UDL_REG_VSYNCEND, udl_lfsr(val)); 1770 } else { 1771 udl_reg_write_2(sc, UDL_REG_VSYNCSTART, udl_lfsr(val)); 1772 udl_reg_write_2(sc, UDL_REG_VSYNCEND, udl_lfsr(0)); 1773 } 1774 val = vmp->vdisplay; 1775 udl_reg_write_2(sc, UDL_REG_VPIXELS, val); 1776 val = vmp->dot_clock / 5; 1777 udl_reg_write_2(sc, UDL_REG_PIXELCLOCK5KHZ, bswap16(val)); 1778 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 1779 1780 if (udl_cmd_send(sc) != 0) 1781 return -1; 1782 1783 /* clear screen */ 1784 udl_fill_rect(sc, 0, 0, 0, sc->sc_width, sc->sc_height); 1785 1786 if (udl_cmd_send(sc) != 0) 1787 return -1; 1788 1789 /* show framebuffer content */ 1790 udl_blank(sc, 0); 1791 1792 if (udl_cmd_send(sc) != 0) 1793 return -1; 1794 1795 sc->sc_blank = WSDISPLAYIO_VIDEO_ON; 1796 1797 return 0; 1798 } 1799 1800 static const struct videomode * 1801 udl_videomode_lookup(const char *name) 1802 { 1803 int i; 1804 1805 for (i = 0; i < videomode_count; i++) 1806 if (strcmp(name, videomode_list[i].name) == 0) 1807 return &videomode_list[i]; 1808 1809 return NULL; 1810 } 1811 1812 static void 1813 udl_update_thread(void *v) 1814 { 1815 struct udl_softc *sc = v; 1816 int stride; 1817 #ifdef notyet 1818 bool update = false; 1819 int linecount, x, y; 1820 uint16_t *fb, *fbcopy; 1821 uint8_t *curfb; 1822 #else 1823 uint16_t *fb; 1824 int offs; 1825 #endif 1826 1827 mutex_enter(&sc->sc_thread_mtx); 1828 1829 for (;;) { 1830 stride = min(sc->sc_width, UDL_CMD_WIDTH_MAX - 8); 1831 if (sc->sc_dying == true) { 1832 mutex_exit(&sc->sc_thread_mtx); 1833 kthread_exit(0); 1834 } 1835 1836 if (sc->sc_thread_stop == true || sc->sc_fbmem == NULL) 1837 goto thread_wait; 1838 1839 #ifdef notyet 1840 curfb = kmem_zalloc(UDL_FBMEM_SIZE(sc), KM_SLEEP); 1841 memcpy(curfb, sc->sc_fbmem, sc->sc_height * sc->sc_width * 2); 1842 fb = (uint16_t *)curfb; 1843 fbcopy = (uint16_t *)sc->sc_fbmem_prev; 1844 for (y = 0; y < sc->sc_height; y++) { 1845 linecount = 0; 1846 update = false; 1847 for (x = 0; x < sc->sc_width; x++) { 1848 if (linecount >= stride) { 1849 udl_draw_line(sc, &fb[y * sc->sc_width 1850 + x - linecount], y * sc->sc_width 1851 + x - linecount, linecount); 1852 linecount = 0; 1853 update = false; 1854 } 1855 if (fb[y * sc->sc_width + x] ^ fbcopy[y * 1856 sc->sc_width + x]) { 1857 update = true; 1858 linecount ++; 1859 } else if (update == true) { 1860 udl_draw_line(sc, &fb[y * sc->sc_width 1861 + x - linecount], y * sc->sc_width 1862 + x - linecount, linecount); 1863 linecount = 0; 1864 update = false; 1865 } 1866 } 1867 if (linecount) { 1868 udl_draw_line(sc, &fb[y * sc->sc_width + x - 1869 linecount], y * sc->sc_width + x - 1870 linecount, linecount); 1871 } 1872 } 1873 memcpy(sc->sc_fbmem_prev, curfb, sc->sc_height * sc->sc_width 1874 * 2); 1875 kmem_free(curfb, UDL_FBMEM_SIZE(sc)); 1876 #else 1877 fb = (uint16_t *)sc->sc_fbmem; 1878 for (offs = 0; offs < sc->sc_height * sc->sc_width; offs += stride) 1879 udl_draw_line(sc, &fb[offs], offs, stride); 1880 1881 #endif 1882 1883 kpause("udlslp", false, (40 * hz)/1000 + 1, &sc->sc_thread_mtx); 1884 continue; 1885 1886 thread_wait: 1887 cv_wait(&sc->sc_thread_cv, &sc->sc_thread_mtx); 1888 } 1889 } 1890 1891 static inline void 1892 udl_startstop(struct udl_softc *sc, bool stop) 1893 { 1894 mutex_enter(&sc->sc_thread_mtx); 1895 sc->sc_thread_stop = stop; 1896 if (!stop) 1897 cv_broadcast(&sc->sc_thread_cv); 1898 mutex_exit(&sc->sc_thread_mtx); 1899 } 1900