1*6ae24b2bStsutsui /* $NetBSD: udl.c,v 1.35 2024/10/02 17:22:45 tsutsui Exp $ */ 247f87355Stsutsui 347f87355Stsutsui /*- 447f87355Stsutsui * Copyright (c) 2009 FUKAUMI Naoki. 547f87355Stsutsui * All rights reserved. 647f87355Stsutsui * 747f87355Stsutsui * Redistribution and use in source and binary forms, with or without 847f87355Stsutsui * modification, are permitted provided that the following conditions 947f87355Stsutsui * are met: 1047f87355Stsutsui * 1. Redistributions of source code must retain the above copyright 1147f87355Stsutsui * notice, this list of conditions and the following disclaimer. 1247f87355Stsutsui * 2. Redistributions in binary form must reproduce the above copyright 1347f87355Stsutsui * notice, this list of conditions and the following disclaimer in the 1447f87355Stsutsui * documentation and/or other materials provided with the distribution. 1547f87355Stsutsui * 1647f87355Stsutsui * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1747f87355Stsutsui * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1847f87355Stsutsui * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1947f87355Stsutsui * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2047f87355Stsutsui * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2147f87355Stsutsui * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2247f87355Stsutsui * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2347f87355Stsutsui * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2447f87355Stsutsui * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2547f87355Stsutsui * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2647f87355Stsutsui */ 2747f87355Stsutsui 2847f87355Stsutsui /* 2947f87355Stsutsui * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org> 3047f87355Stsutsui * 3147f87355Stsutsui * Permission to use, copy, modify, and distribute this software for any 3247f87355Stsutsui * purpose with or without fee is hereby granted, provided that the above 3347f87355Stsutsui * copyright notice and this permission notice appear in all copies. 3447f87355Stsutsui * 3547f87355Stsutsui * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 3647f87355Stsutsui * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 3747f87355Stsutsui * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 3847f87355Stsutsui * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 3947f87355Stsutsui * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 4047f87355Stsutsui * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 4147f87355Stsutsui * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 4247f87355Stsutsui */ 4347f87355Stsutsui 4447f87355Stsutsui /* 4547f87355Stsutsui * Driver for the ``DisplayLink DL-1x0 / DL-1x5'' graphic chips based 4647f87355Stsutsui * on the reversed engineered specifications of Florian Echtler 4747f87355Stsutsui * <floe at butterbrot dot org>: 4847f87355Stsutsui * 4947f87355Stsutsui * http://floe.butterbrot.org/displaylink/doku.php 5047f87355Stsutsui * 5147f87355Stsutsui * This driver was written by Marcus Glocker for OpenBSD and ported to 5247f87355Stsutsui * NetBSD by FUKAUMI Naoki with many modification. 5347f87355Stsutsui */ 5447f87355Stsutsui 5547f87355Stsutsui #include <sys/cdefs.h> 56*6ae24b2bStsutsui __KERNEL_RCSID(0, "$NetBSD: udl.c,v 1.35 2024/10/02 17:22:45 tsutsui Exp $"); 57a7c71d30Sskrll 58a7c71d30Sskrll #ifdef _KERNEL_OPT 59a7c71d30Sskrll #include "opt_usb.h" 60a7c71d30Sskrll #endif 6147f87355Stsutsui 6247f87355Stsutsui #include <sys/param.h> 6347f87355Stsutsui #include <sys/device.h> 6447f87355Stsutsui #include <sys/kernel.h> 6547f87355Stsutsui #include <sys/proc.h> 6647f87355Stsutsui #include <sys/systm.h> 6747f87355Stsutsui #include <sys/kmem.h> 687b977f0dSnat #include <sys/kthread.h> 697b977f0dSnat #include <sys/condvar.h> 7047f87355Stsutsui 7147f87355Stsutsui #include <sys/bus.h> 7247f87355Stsutsui #include <sys/endian.h> 7347f87355Stsutsui 749fc45356Sriastradh #include <uvm/uvm_extern.h> 759fc45356Sriastradh 7647f87355Stsutsui #include <dev/usb/usb.h> 7747f87355Stsutsui #include <dev/usb/usbdi.h> 7847f87355Stsutsui #include <dev/usb/usbdivar.h> 7947f87355Stsutsui #include <dev/usb/usbdi_util.h> 8047f87355Stsutsui #include <dev/usb/usb_mem.h> 8147f87355Stsutsui #include <dev/usb/usbdevs.h> 8247f87355Stsutsui 8347f87355Stsutsui #include <dev/firmload.h> 8447f87355Stsutsui 8547f87355Stsutsui #include <dev/videomode/videomode.h> 8647f87355Stsutsui #include <dev/videomode/edidvar.h> 8747f87355Stsutsui 8847f87355Stsutsui #include <dev/wscons/wsconsio.h> 8947f87355Stsutsui #include <dev/wscons/wsdisplayvar.h> 9047f87355Stsutsui #include <dev/rasops/rasops.h> 9147f87355Stsutsui 9247f87355Stsutsui #include <dev/usb/udl.h> 9347f87355Stsutsui #ifdef notyet 9447f87355Stsutsui #include <dev/usb/udlio.h> 9547f87355Stsutsui #endif 9647f87355Stsutsui 9747f87355Stsutsui /* 9847f87355Stsutsui * Defines. 9947f87355Stsutsui */ 10047f87355Stsutsui #ifdef UDL_DEBUG 10147f87355Stsutsui #define DPRINTF(x) do { if (udl_debug) printf x; } while (0) 10247f87355Stsutsui #define DPRINTFN(n, x) do { if (udl_debug >= (n)) printf x; } while (0) 10347f87355Stsutsui int udl_debug = 1; 10447f87355Stsutsui #else 10547f87355Stsutsui #define DPRINTF(x) do {} while (0) 10647f87355Stsutsui #define DPRINTFN(n, x) do {} while (0) 10747f87355Stsutsui #endif 10847f87355Stsutsui 10947f87355Stsutsui /* 11047f87355Stsutsui * Prototypes. 11147f87355Stsutsui */ 11247f87355Stsutsui static int udl_match(device_t, cfdata_t, void *); 11347f87355Stsutsui static void udl_attach(device_t, device_t, void *); 11447f87355Stsutsui static int udl_detach(device_t, int); 11547f87355Stsutsui 11647f87355Stsutsui static int udl_ioctl(void *, void *, u_long, void *, int, 11747f87355Stsutsui struct lwp *); 11847f87355Stsutsui static paddr_t udl_mmap(void *, void *, off_t, int); 11947f87355Stsutsui static int udl_alloc_screen(void *, const struct wsscreen_descr *, 12047f87355Stsutsui void **, int *, int *, long *); 12147f87355Stsutsui static void udl_free_screen(void *, void *); 12247f87355Stsutsui static int udl_show_screen(void *, void *, int, 12347f87355Stsutsui void (*)(void *, int, int), void *); 12447f87355Stsutsui 12547f87355Stsutsui static void udl_comp_load(struct udl_softc *); 12647f87355Stsutsui static void udl_comp_unload(struct udl_softc *); 12747f87355Stsutsui static int udl_fbmem_alloc(struct udl_softc *); 12847f87355Stsutsui static void udl_fbmem_free(struct udl_softc *); 12947f87355Stsutsui static int udl_cmdq_alloc(struct udl_softc *); 13047f87355Stsutsui static void udl_cmdq_free(struct udl_softc *); 13147f87355Stsutsui static struct udl_cmdq *udl_cmdq_get(struct udl_softc *sc); 13247f87355Stsutsui static void udl_cmdq_put(struct udl_softc *sc, 13347f87355Stsutsui struct udl_cmdq *cmdq); 13447f87355Stsutsui static void udl_cmdq_flush(struct udl_softc *); 13547f87355Stsutsui 13647f87355Stsutsui static void udl_cursor(void *, int, int, int); 13747f87355Stsutsui static void udl_putchar(void *, int, int, u_int, long); 13847f87355Stsutsui static void udl_copycols(void *, int, int, int, int); 13947f87355Stsutsui static void udl_erasecols(void *, int, int, int, long); 14047f87355Stsutsui static void udl_copyrows(void *, int, int, int); 14147f87355Stsutsui static void udl_eraserows(void *, int, int, long); 14247f87355Stsutsui 14347f87355Stsutsui static void udl_restore_char(struct rasops_info *); 14447f87355Stsutsui static void udl_draw_char(struct rasops_info *, uint16_t *, u_int, 14547f87355Stsutsui int, int); 14647f87355Stsutsui static void udl_copy_rect(struct udl_softc *, int, int, int, int, 14747f87355Stsutsui int, int); 14847f87355Stsutsui static void udl_fill_rect(struct udl_softc *, uint16_t, int, int, 14947f87355Stsutsui int, int); 15047f87355Stsutsui #ifdef notyet 15147f87355Stsutsui static void udl_draw_rect(struct udl_softc *, 15247f87355Stsutsui struct udl_ioctl_damage *); 15347f87355Stsutsui static void udl_draw_rect_comp(struct udl_softc *, 15447f87355Stsutsui struct udl_ioctl_damage *); 15547f87355Stsutsui #endif 15647f87355Stsutsui 15747f87355Stsutsui static inline void udl_copy_line(struct udl_softc *, int, int, int); 15847f87355Stsutsui static inline void udl_fill_line(struct udl_softc *, uint16_t, int, int); 15947f87355Stsutsui static inline void udl_draw_line(struct udl_softc *, uint16_t *, int, 16047f87355Stsutsui int); 161d81879ebSjoerg #ifdef notyet 16247f87355Stsutsui static inline void udl_draw_line_comp(struct udl_softc *, uint16_t *, int, 16347f87355Stsutsui int); 164d81879ebSjoerg #endif 16547f87355Stsutsui 16647f87355Stsutsui static int udl_cmd_send(struct udl_softc *); 16747f87355Stsutsui static void udl_cmd_send_async(struct udl_softc *); 1684e8e6643Sskrll static void udl_cmd_send_async_cb(struct usbd_xfer *, 1694e8e6643Sskrll void *, usbd_status); 17047f87355Stsutsui 17147f87355Stsutsui static int udl_ctrl_msg(struct udl_softc *, uint8_t, uint8_t, 17247f87355Stsutsui uint16_t, uint16_t, uint8_t *, uint16_t); 17347f87355Stsutsui static int udl_init(struct udl_softc *); 17447f87355Stsutsui static void udl_read_edid(struct udl_softc *); 17547f87355Stsutsui static void udl_set_address(struct udl_softc *, int, int, int, 17647f87355Stsutsui int); 17747f87355Stsutsui static void udl_blank(struct udl_softc *, int); 17847f87355Stsutsui static uint16_t udl_lfsr(uint16_t); 17947f87355Stsutsui static int udl_set_resolution(struct udl_softc *, 18047f87355Stsutsui const struct videomode *); 18147f87355Stsutsui static const struct videomode *udl_videomode_lookup(const char *); 1827b977f0dSnat static void udl_update_thread(void *); 183c1b9bee0Snat static inline void udl_startstop(struct udl_softc *, bool); 18447f87355Stsutsui 18547f87355Stsutsui static inline void 18647f87355Stsutsui udl_cmd_add_1(struct udl_softc *sc, uint8_t val) 18747f87355Stsutsui { 18847f87355Stsutsui 18947f87355Stsutsui *sc->sc_cmd_buf++ = val; 19047f87355Stsutsui } 19147f87355Stsutsui 19247f87355Stsutsui static inline void 19347f87355Stsutsui udl_cmd_add_2(struct udl_softc *sc, uint16_t val) 19447f87355Stsutsui { 19547f87355Stsutsui 19647f87355Stsutsui be16enc(sc->sc_cmd_buf, val); 19747f87355Stsutsui sc->sc_cmd_buf += 2; 19847f87355Stsutsui } 19947f87355Stsutsui 20047f87355Stsutsui static inline void 20147f87355Stsutsui udl_cmd_add_3(struct udl_softc *sc, uint32_t val) 20247f87355Stsutsui { 20347f87355Stsutsui 20447f87355Stsutsui udl_cmd_add_2(sc, val >> 8); 20547f87355Stsutsui udl_cmd_add_1(sc, val); 20647f87355Stsutsui } 20747f87355Stsutsui 20847f87355Stsutsui static inline void 20947f87355Stsutsui udl_cmd_add_4(struct udl_softc *sc, uint32_t val) 21047f87355Stsutsui { 21147f87355Stsutsui 21247f87355Stsutsui be32enc(sc->sc_cmd_buf, val); 21347f87355Stsutsui sc->sc_cmd_buf += 4; 21447f87355Stsutsui } 21547f87355Stsutsui 21647f87355Stsutsui static inline void 21747f87355Stsutsui udl_cmd_add_buf(struct udl_softc *sc, uint16_t *buf, int width) 21847f87355Stsutsui { 21947f87355Stsutsui #if BYTE_ORDER == BIG_ENDIAN 22047f87355Stsutsui memcpy(sc->sc_cmd_buf, buf, width * 2); 22147f87355Stsutsui sc->sc_cmd_buf += width * 2; 22247f87355Stsutsui #else 22347f87355Stsutsui uint16_t *endp; 22447f87355Stsutsui 22547f87355Stsutsui endp = buf + width; 22647f87355Stsutsui 22747f87355Stsutsui if (((uintptr_t)sc->sc_cmd_buf & 1) == 0) { 22847f87355Stsutsui while (buf < endp) { 22947f87355Stsutsui *(uint16_t *)sc->sc_cmd_buf = htobe16(*buf++); 23047f87355Stsutsui sc->sc_cmd_buf += 2; 23147f87355Stsutsui } 23247f87355Stsutsui } else { 23347f87355Stsutsui while (buf < endp) { 23447f87355Stsutsui be16enc(sc->sc_cmd_buf, *buf++); 23547f87355Stsutsui sc->sc_cmd_buf += 2; 23647f87355Stsutsui } 23747f87355Stsutsui } 23847f87355Stsutsui #endif 23947f87355Stsutsui } 24047f87355Stsutsui 24147f87355Stsutsui static inline void 24247f87355Stsutsui udl_reg_write_1(struct udl_softc *sc, uint8_t reg, uint8_t val) 24347f87355Stsutsui { 24447f87355Stsutsui 24547f87355Stsutsui udl_cmd_add_4(sc, (UDL_BULK_SOC << 24) | 24647f87355Stsutsui (UDL_BULK_CMD_REG_WRITE_1 << 16) | (reg << 8) | val); 24747f87355Stsutsui } 24847f87355Stsutsui 24947f87355Stsutsui static inline void 25047f87355Stsutsui udl_reg_write_2(struct udl_softc *sc, uint8_t reg, uint16_t val) 25147f87355Stsutsui { 25247f87355Stsutsui 25347f87355Stsutsui udl_reg_write_1(sc, reg++, val >> 8); 25447f87355Stsutsui udl_reg_write_1(sc, reg, val); 25547f87355Stsutsui } 25647f87355Stsutsui 25747f87355Stsutsui static inline void 25847f87355Stsutsui udl_reg_write_3(struct udl_softc *sc, uint8_t reg, uint32_t val) 25947f87355Stsutsui { 26047f87355Stsutsui 26147f87355Stsutsui udl_reg_write_1(sc, reg++, val >> 16); 26247f87355Stsutsui udl_reg_write_1(sc, reg++, val >> 8); 26347f87355Stsutsui udl_reg_write_1(sc, reg, val); 26447f87355Stsutsui } 26547f87355Stsutsui 26647f87355Stsutsui /* XXX */ 26747f87355Stsutsui static int 26847f87355Stsutsui firmware_load(const char *dname, const char *iname, uint8_t **ucodep, 26947f87355Stsutsui size_t *sizep) 27047f87355Stsutsui { 27147f87355Stsutsui firmware_handle_t fh; 27247f87355Stsutsui int error; 27347f87355Stsutsui 27447f87355Stsutsui if ((error = firmware_open(dname, iname, &fh)) != 0) 27547f87355Stsutsui return error; 27647f87355Stsutsui *sizep = firmware_get_size(fh); 27747f87355Stsutsui if ((*ucodep = firmware_malloc(*sizep)) == NULL) { 27847f87355Stsutsui firmware_close(fh); 27947f87355Stsutsui return ENOMEM; 28047f87355Stsutsui } 28147f87355Stsutsui if ((error = firmware_read(fh, 0, *ucodep, *sizep)) != 0) 28247f87355Stsutsui firmware_free(*ucodep, *sizep); 28347f87355Stsutsui firmware_close(fh); 28447f87355Stsutsui 28547f87355Stsutsui return error; 28647f87355Stsutsui } 28747f87355Stsutsui 28847f87355Stsutsui /* 28947f87355Stsutsui * Driver glue. 29047f87355Stsutsui */ 29147f87355Stsutsui CFATTACH_DECL_NEW(udl, sizeof(struct udl_softc), 29247f87355Stsutsui udl_match, udl_attach, udl_detach, NULL); 29347f87355Stsutsui 29447f87355Stsutsui /* 29547f87355Stsutsui * wsdisplay glue. 29647f87355Stsutsui */ 29747f87355Stsutsui static struct wsdisplay_accessops udl_accessops = { 29847f87355Stsutsui udl_ioctl, 29947f87355Stsutsui udl_mmap, 30047f87355Stsutsui udl_alloc_screen, 30147f87355Stsutsui udl_free_screen, 30247f87355Stsutsui udl_show_screen, 30347f87355Stsutsui NULL, 30447f87355Stsutsui NULL, 30547f87355Stsutsui NULL, 30647f87355Stsutsui }; 30747f87355Stsutsui 30847f87355Stsutsui /* 30947f87355Stsutsui * Matching devices. 31047f87355Stsutsui */ 31147f87355Stsutsui static const struct usb_devno udl_devs[] = { 31256f5151eSnonaka { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020 }, 31347f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220 }, 314b099331dSdrochner { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD190 }, 31547f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_U70 }, 316abd564ccSmsaitoh { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2 }, 31747f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60 }, 31856f5151eSnonaka { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV }, 31947f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI }, 32047f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_USBRGB }, 32147f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCDUSB7X }, 32256f5151eSnonaka { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCDUSB10X }, 32347f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10 }, 32447f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI }, 32547f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008 }, 32647f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GXDVIU2 }, 32778c40df0Smsaitoh { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GXDVIU2B }, 32847f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U }, 32947f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U }, 33047f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK }, 33156f5151eSnonaka { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571 }, 33247f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061 }, 33356f5151eSnonaka { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK }, 33447f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI }, 33556f5151eSnonaka { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70 }, 3362733867fSnonaka { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000UD_DVI }, 33747f87355Stsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LDEWX015U }, 338*6ae24b2bStsutsui { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_KC002N }, 339abd564ccSmsaitoh { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_MIMO }, 340abd564ccSmsaitoh { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE }, 3412d605feeSnonaka { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421WIDE }, 34242039c0dSchristos { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SD_U2VDH }, 34323539045Snat { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0 }, 34423539045Snat { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_FYDVI }, 34523539045Snat { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_FYDVI2 } 34647f87355Stsutsui }; 34747f87355Stsutsui 34847f87355Stsutsui static int 34947f87355Stsutsui udl_match(device_t parent, cfdata_t match, void *aux) 35047f87355Stsutsui { 35147f87355Stsutsui struct usb_attach_arg *uaa = aux; 35247f87355Stsutsui 3534e8e6643Sskrll if (usb_lookup(udl_devs, uaa->uaa_vendor, uaa->uaa_product) != NULL) 35447f87355Stsutsui return UMATCH_VENDOR_PRODUCT; 35547f87355Stsutsui 35647f87355Stsutsui return UMATCH_NONE; 35747f87355Stsutsui } 35847f87355Stsutsui 35947f87355Stsutsui static void 36047f87355Stsutsui udl_attach(device_t parent, device_t self, void *aux) 36147f87355Stsutsui { 36247f87355Stsutsui struct udl_softc *sc = device_private(self); 36347f87355Stsutsui struct usb_attach_arg *uaa = aux; 36447f87355Stsutsui struct wsemuldisplaydev_attach_args aa; 36547f87355Stsutsui const struct videomode *vmp; 36647f87355Stsutsui usbd_status error; 36747f87355Stsutsui char *devinfop; 36847f87355Stsutsui 36947f87355Stsutsui aprint_naive("\n"); 37047f87355Stsutsui aprint_normal("\n"); 37147f87355Stsutsui 37247f87355Stsutsui sc->sc_dev = self; 3734e8e6643Sskrll sc->sc_udev = uaa->uaa_device; 374ce2468caSmaxv sc->sc_init_state = UDL_INIT_NONE; 37547f87355Stsutsui 37647f87355Stsutsui devinfop = usbd_devinfo_alloc(sc->sc_udev, 0); 37747f87355Stsutsui aprint_normal_dev(sc->sc_dev, "%s\n", devinfop); 37847f87355Stsutsui usbd_devinfo_free(devinfop); 37947f87355Stsutsui 38047f87355Stsutsui /* 38147f87355Stsutsui * Set device configuration descriptor number. 38247f87355Stsutsui */ 38347f87355Stsutsui error = usbd_set_config_no(sc->sc_udev, 1, 0); 384897388eaSskrll if (error != USBD_NORMAL_COMPLETION) { 385897388eaSskrll aprint_error_dev(self, "failed to set configuration" 386897388eaSskrll ", err=%s\n", usbd_errstr(error)); 38747f87355Stsutsui return; 388897388eaSskrll } 38947f87355Stsutsui 39047f87355Stsutsui /* 39147f87355Stsutsui * Create device handle to interface descriptor. 39247f87355Stsutsui */ 39347f87355Stsutsui error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface); 39447f87355Stsutsui if (error != USBD_NORMAL_COMPLETION) 39547f87355Stsutsui return; 39647f87355Stsutsui 39747f87355Stsutsui /* 39847f87355Stsutsui * Open bulk TX pipe. 39947f87355Stsutsui */ 40047f87355Stsutsui error = usbd_open_pipe(sc->sc_iface, 1, USBD_EXCLUSIVE_USE, 40147f87355Stsutsui &sc->sc_tx_pipeh); 40247f87355Stsutsui if (error != USBD_NORMAL_COMPLETION) 40347f87355Stsutsui return; 40447f87355Stsutsui 40547f87355Stsutsui #ifdef UDL_EVENT_COUNTERS 40647f87355Stsutsui evcnt_attach_dynamic(&sc->sc_ev_cmdq_get, EVCNT_TYPE_MISC, NULL, 40747f87355Stsutsui device_xname(sc->sc_dev), "udl_cmdq_get"); 40847f87355Stsutsui evcnt_attach_dynamic(&sc->sc_ev_cmdq_put, EVCNT_TYPE_MISC, NULL, 40947f87355Stsutsui device_xname(sc->sc_dev), "udl_cmdq_put"); 41047f87355Stsutsui evcnt_attach_dynamic(&sc->sc_ev_cmdq_wait, EVCNT_TYPE_MISC, NULL, 41147f87355Stsutsui device_xname(sc->sc_dev), "udl_cmdq_wait"); 41247f87355Stsutsui evcnt_attach_dynamic(&sc->sc_ev_cmdq_timeout, EVCNT_TYPE_MISC, NULL, 41347f87355Stsutsui device_xname(sc->sc_dev), "udl_cmdq_timeout"); 41447f87355Stsutsui #endif 41547f87355Stsutsui cv_init(&sc->sc_cv, device_xname(sc->sc_dev)); 41647f87355Stsutsui mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_TTY); /* XXX for tty_lock */ 417ce2468caSmaxv sc->sc_init_state = UDL_INIT_MIDWAY; 418ce2468caSmaxv 419ce2468caSmaxv /* 420ce2468caSmaxv * Allocate bulk command queue. 421ce2468caSmaxv */ 422ce2468caSmaxv if (udl_cmdq_alloc(sc) != 0) 423ce2468caSmaxv return; 42447f87355Stsutsui 42547f87355Stsutsui if ((sc->sc_cmd_cur = udl_cmdq_get(sc)) == NULL) 42647f87355Stsutsui return; 42747f87355Stsutsui UDL_CMD_BUFINIT(sc); 42847f87355Stsutsui 42947f87355Stsutsui /* 43047f87355Stsutsui * Initialize chip. 43147f87355Stsutsui */ 43247f87355Stsutsui if (udl_init(sc) != 0) 43347f87355Stsutsui return; 43447f87355Stsutsui 43547f87355Stsutsui udl_read_edid(sc); 43647f87355Stsutsui 43747f87355Stsutsui /* 43847f87355Stsutsui * Initialize resolution. 43947f87355Stsutsui */ 44047f87355Stsutsui #ifndef UDL_VIDEOMODE 44147f87355Stsutsui if (sc->sc_ei.edid_nmodes != 0 && 44247f87355Stsutsui sc->sc_ei.edid_preferred_mode != NULL) 44347f87355Stsutsui vmp = sc->sc_ei.edid_preferred_mode; 44447f87355Stsutsui else 44547f87355Stsutsui #define UDL_VIDEOMODE "640x480x60" 44647f87355Stsutsui #endif 44747f87355Stsutsui vmp = udl_videomode_lookup(UDL_VIDEOMODE); 44847f87355Stsutsui 44947f87355Stsutsui if (vmp == NULL) 45047f87355Stsutsui return; 45147f87355Stsutsui 45247f87355Stsutsui sc->sc_width = vmp->hdisplay; 45347f87355Stsutsui sc->sc_height = vmp->vdisplay; 45447f87355Stsutsui sc->sc_offscreen = sc->sc_height * 3 / 2; 45547f87355Stsutsui sc->sc_depth = 16; 45647f87355Stsutsui 45747f87355Stsutsui if (udl_set_resolution(sc, vmp) != 0) 45847f87355Stsutsui return; 45947f87355Stsutsui 46047f87355Stsutsui sc->sc_defaultscreen.name = "default"; 46147f87355Stsutsui sc->sc_screens[0] = &sc->sc_defaultscreen; 46247f87355Stsutsui sc->sc_screenlist.nscreens = 1; 46347f87355Stsutsui sc->sc_screenlist.screens = sc->sc_screens; 46447f87355Stsutsui 46547f87355Stsutsui /* 46647f87355Stsutsui * Set initial wsdisplay emulation mode. 46747f87355Stsutsui */ 46847f87355Stsutsui sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 46947f87355Stsutsui 47047f87355Stsutsui /* 47147f87355Stsutsui * Attach wsdisplay. 47247f87355Stsutsui */ 47347f87355Stsutsui aa.console = 0; 47447f87355Stsutsui aa.scrdata = &sc->sc_screenlist; 47547f87355Stsutsui aa.accessops = &udl_accessops; 47647f87355Stsutsui aa.accesscookie = sc; 47747f87355Stsutsui 47847f87355Stsutsui sc->sc_wsdisplay = 479c7fb772bSthorpej config_found(sc->sc_dev, &aa, wsemuldisplaydevprint, CFARGS_NONE); 48047f87355Stsutsui 48147f87355Stsutsui usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); 4827b977f0dSnat 4837b977f0dSnat mutex_init(&sc->sc_thread_mtx, MUTEX_DEFAULT, IPL_NONE); 4847b977f0dSnat cv_init(&sc->sc_thread_cv, "udlcv"); 4857b977f0dSnat sc->sc_dying = false; 486c1b9bee0Snat sc->sc_thread_stop = true; 4877b977f0dSnat kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, 4887b977f0dSnat udl_update_thread, sc, &sc->sc_thread, "udlupd"); 489ce2468caSmaxv 490ce2468caSmaxv sc->sc_init_state = UDL_INIT_INITED; 49147f87355Stsutsui } 49247f87355Stsutsui 49347f87355Stsutsui static int 49447f87355Stsutsui udl_detach(device_t self, int flags) 49547f87355Stsutsui { 49647f87355Stsutsui struct udl_softc *sc = device_private(self); 49747f87355Stsutsui 49847f87355Stsutsui /* 49947f87355Stsutsui * Close bulk TX pipe. 50047f87355Stsutsui */ 50147f87355Stsutsui if (sc->sc_tx_pipeh != NULL) { 50247f87355Stsutsui usbd_abort_pipe(sc->sc_tx_pipeh); 50347f87355Stsutsui } 50447f87355Stsutsui 505ce2468caSmaxv if (sc->sc_init_state >= UDL_INIT_MIDWAY) { 50647f87355Stsutsui /* 50747f87355Stsutsui * Free command xfer buffers. 50847f87355Stsutsui */ 50947f87355Stsutsui udl_cmdq_flush(sc); 51047f87355Stsutsui udl_cmdq_free(sc); 511ce2468caSmaxv } 51247f87355Stsutsui 5134e8e6643Sskrll if (sc->sc_tx_pipeh != NULL) { 5144e8e6643Sskrll usbd_close_pipe(sc->sc_tx_pipeh); 5154e8e6643Sskrll } 5164e8e6643Sskrll 51747f87355Stsutsui /* 51847f87355Stsutsui * Free Huffman table. 51947f87355Stsutsui */ 52047f87355Stsutsui udl_comp_unload(sc); 52147f87355Stsutsui 522ce2468caSmaxv if (sc->sc_init_state >= UDL_INIT_INITED) { 52347f87355Stsutsui /* 52447f87355Stsutsui * Free framebuffer memory. 52547f87355Stsutsui */ 52647f87355Stsutsui udl_fbmem_free(sc); 52747f87355Stsutsui 528c1b9bee0Snat mutex_enter(&sc->sc_thread_mtx); 5297b977f0dSnat sc->sc_dying = true; 5307b977f0dSnat cv_broadcast(&sc->sc_thread_cv); 531c1b9bee0Snat mutex_exit(&sc->sc_thread_mtx); 5327b977f0dSnat kthread_join(sc->sc_thread); 5337b977f0dSnat cv_destroy(&sc->sc_thread_cv); 5347b977f0dSnat mutex_destroy(&sc->sc_thread_mtx); 535ce2468caSmaxv } 5367b977f0dSnat 537ce2468caSmaxv if (sc->sc_init_state >= UDL_INIT_MIDWAY) { 538ce2468caSmaxv cv_destroy(&sc->sc_cv); 539ce2468caSmaxv mutex_destroy(&sc->sc_mtx); 540ce2468caSmaxv } 541ce2468caSmaxv 542ce2468caSmaxv if (sc->sc_init_state >= UDL_INIT_INITED) { 54347f87355Stsutsui /* 54447f87355Stsutsui * Detach wsdisplay. 54547f87355Stsutsui */ 54647f87355Stsutsui if (sc->sc_wsdisplay != NULL) 54747f87355Stsutsui config_detach(sc->sc_wsdisplay, DETACH_FORCE); 54847f87355Stsutsui 549ce2468caSmaxv usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 550ce2468caSmaxv sc->sc_dev); 551ce2468caSmaxv } 55247f87355Stsutsui 553ce2468caSmaxv if (sc->sc_init_state >= UDL_INIT_MIDWAY) { 55447f87355Stsutsui #ifdef UDL_EVENT_COUNTERS 55547f87355Stsutsui evcnt_detach(&sc->sc_ev_cmdq_get); 55647f87355Stsutsui evcnt_detach(&sc->sc_ev_cmdq_put); 55747f87355Stsutsui evcnt_detach(&sc->sc_ev_cmdq_wait); 55847f87355Stsutsui evcnt_detach(&sc->sc_ev_cmdq_timeout); 55947f87355Stsutsui #endif 560ce2468caSmaxv } 56147f87355Stsutsui 56247f87355Stsutsui return 0; 56347f87355Stsutsui } 56447f87355Stsutsui 56547f87355Stsutsui static int 56647f87355Stsutsui udl_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l) 56747f87355Stsutsui { 56847f87355Stsutsui struct udl_softc *sc = v; 56947f87355Stsutsui #ifdef notyet 57047f87355Stsutsui struct udl_ioctl_damage *d; 57147f87355Stsutsui #endif 57247f87355Stsutsui struct wsdisplay_fbinfo *wdf; 57347f87355Stsutsui u_int mode; 57447f87355Stsutsui 57547f87355Stsutsui switch (cmd) { 57647f87355Stsutsui case WSDISPLAYIO_GTYPE: 57747f87355Stsutsui *(u_int *)data = WSDISPLAY_TYPE_DL; 57847f87355Stsutsui return 0; 57947f87355Stsutsui 58047f87355Stsutsui case WSDISPLAYIO_GINFO: 58147f87355Stsutsui wdf = (struct wsdisplay_fbinfo *)data; 58247f87355Stsutsui wdf->height = sc->sc_height; 58347f87355Stsutsui wdf->width = sc->sc_width; 58447f87355Stsutsui wdf->depth = sc->sc_depth; 58547f87355Stsutsui wdf->cmsize = 0; 58647f87355Stsutsui return 0; 58747f87355Stsutsui 58847f87355Stsutsui case WSDISPLAYIO_GVIDEO: 58947f87355Stsutsui *(u_int *)data = sc->sc_blank; 59047f87355Stsutsui return 0; 59147f87355Stsutsui 59247f87355Stsutsui case WSDISPLAYIO_SVIDEO: 59347f87355Stsutsui mode = *(u_int *)data; 59447f87355Stsutsui if (mode == sc->sc_blank) 59547f87355Stsutsui return 0; 59647f87355Stsutsui switch (mode) { 59747f87355Stsutsui case WSDISPLAYIO_VIDEO_OFF: 598c1b9bee0Snat udl_startstop(sc, true); 59947f87355Stsutsui udl_blank(sc, 1); 60047f87355Stsutsui break; 60147f87355Stsutsui case WSDISPLAYIO_VIDEO_ON: 60247f87355Stsutsui udl_blank(sc, 0); 60347f87355Stsutsui break; 60447f87355Stsutsui default: 60547f87355Stsutsui return EINVAL; 60647f87355Stsutsui } 607c1b9bee0Snat if (UDL_CMD_BUFSIZE(sc) > 0) 60847f87355Stsutsui udl_cmd_send_async(sc); 60947f87355Stsutsui udl_cmdq_flush(sc); 61047f87355Stsutsui sc->sc_blank = mode; 61147f87355Stsutsui return 0; 61247f87355Stsutsui 61347f87355Stsutsui case WSDISPLAYIO_SMODE: 61447f87355Stsutsui mode = *(u_int *)data; 61547f87355Stsutsui if (mode == sc->sc_mode) 61647f87355Stsutsui return 0; 61747f87355Stsutsui switch (mode) { 61847f87355Stsutsui case WSDISPLAYIO_MODE_EMUL: 619c1b9bee0Snat udl_startstop(sc, true); 62047f87355Stsutsui /* clear screen */ 62147f87355Stsutsui udl_fill_rect(sc, 0, 0, 0, sc->sc_width, 62247f87355Stsutsui sc->sc_height); 623c1b9bee0Snat if (UDL_CMD_BUFSIZE(sc) > 0) 62447f87355Stsutsui udl_cmd_send_async(sc); 62547f87355Stsutsui udl_cmdq_flush(sc); 62647f87355Stsutsui udl_comp_unload(sc); 62747f87355Stsutsui break; 62847f87355Stsutsui case WSDISPLAYIO_MODE_DUMBFB: 62947f87355Stsutsui if (UDL_CMD_BUFSIZE(sc) > 0) 63047f87355Stsutsui udl_cmd_send_async(sc); 63147f87355Stsutsui udl_cmdq_flush(sc); 63247f87355Stsutsui udl_comp_load(sc); 633c1b9bee0Snat udl_startstop(sc, false); 63447f87355Stsutsui break; 63547f87355Stsutsui default: 63647f87355Stsutsui return EINVAL; 63747f87355Stsutsui } 63847f87355Stsutsui sc->sc_mode = mode; 63947f87355Stsutsui return 0; 64047f87355Stsutsui 64147f87355Stsutsui case WSDISPLAYIO_LINEBYTES: 64247f87355Stsutsui *(u_int *)data = sc->sc_width * (sc->sc_depth / 8); 64347f87355Stsutsui return 0; 64447f87355Stsutsui 64547f87355Stsutsui #ifdef notyet 64647f87355Stsutsui /* 64747f87355Stsutsui * XXX 64847f87355Stsutsui * OpenBSD allows device specific ioctl()s and use this 64947f87355Stsutsui * UDLIO_DAMAGE for the damage extension ops of X servers. 65047f87355Stsutsui * Before blindly pulling such interfaces, probably we should 65147f87355Stsutsui * discuss how such devices should be handled which have 652a0403cdeSmsaitoh * in-direct framebuffer memories that should be transferred 65347f87355Stsutsui * per updated rectangle regions via MI wscons APIs. 65447f87355Stsutsui */ 65547f87355Stsutsui case UDLIO_DAMAGE: 65647f87355Stsutsui d = (struct udl_ioctl_damage *)data; 65747f87355Stsutsui d->status = UDLIO_STATUS_OK; 65847f87355Stsutsui if (sc->sc_flags & UDL_COMPRDY) 65947f87355Stsutsui udl_draw_rect_comp(sc, d); 66047f87355Stsutsui else 66147f87355Stsutsui udl_draw_rect(sc, d); 66247f87355Stsutsui return 0; 66347f87355Stsutsui #endif 66447f87355Stsutsui } 66547f87355Stsutsui 66647f87355Stsutsui return EPASSTHROUGH; 66747f87355Stsutsui } 66847f87355Stsutsui 66947f87355Stsutsui static paddr_t 67047f87355Stsutsui udl_mmap(void *v, void *vs, off_t off, int prot) 67147f87355Stsutsui { 67247f87355Stsutsui struct udl_softc *sc = v; 67347f87355Stsutsui vaddr_t vaddr; 67447f87355Stsutsui paddr_t paddr; 6756205e348Sskrll bool rv __diagused; 67647f87355Stsutsui 67747f87355Stsutsui if (off < 0 || off > roundup2(UDL_FBMEM_SIZE(sc), PAGE_SIZE)) 67847f87355Stsutsui return -1; 67947f87355Stsutsui 68047f87355Stsutsui /* allocate framebuffer memory */ 68147f87355Stsutsui if (udl_fbmem_alloc(sc) != 0) 68247f87355Stsutsui return -1; 68347f87355Stsutsui 684c1b9bee0Snat udl_startstop(sc, false); 685c1b9bee0Snat 68647f87355Stsutsui vaddr = (vaddr_t)sc->sc_fbmem + off; 68747f87355Stsutsui rv = pmap_extract(pmap_kernel(), vaddr, &paddr); 68847f87355Stsutsui KASSERT(rv); 68947f87355Stsutsui paddr += vaddr & PGOFSET; 69047f87355Stsutsui 69147f87355Stsutsui /* XXX we need MI paddr_t -> mmap cookie API */ 692fe33aa27Sryo #if defined(__aarch64__) 693fe33aa27Sryo #define PTOMMAP(paddr) aarch64_btop((char *)paddr) 694fe33aa27Sryo #elif defined(__alpha__) 69547f87355Stsutsui #define PTOMMAP(paddr) alpha_btop((char *)paddr) 69647f87355Stsutsui #elif defined(__arm__) 69747f87355Stsutsui #define PTOMMAP(paddr) arm_btop((u_long)paddr) 69847f87355Stsutsui #elif defined(__hppa__) 69947f87355Stsutsui #define PTOMMAP(paddr) btop((u_long)paddr) 70047f87355Stsutsui #elif defined(__i386__) || defined(__x86_64__) 70147f87355Stsutsui #define PTOMMAP(paddr) x86_btop(paddr) 70247f87355Stsutsui #elif defined(__m68k__) 70347f87355Stsutsui #define PTOMMAP(paddr) m68k_btop((char *)paddr) 70447f87355Stsutsui #elif defined(__mips__) 70547f87355Stsutsui #define PTOMMAP(paddr) mips_btop(paddr) 70647f87355Stsutsui #elif defined(__powerpc__) 70747f87355Stsutsui #define PTOMMAP(paddr) (paddr) 70875b842b8Sskrll #elif defined(__riscv__) 70975b842b8Sskrll #define PTOMMAP(paddr) riscv_btop(paddr) 71047f87355Stsutsui #elif defined(__sh__) 71147f87355Stsutsui #define PTOMMAP(paddr) sh3_btop(paddr) 71247f87355Stsutsui #elif defined(__sparc__) 71347f87355Stsutsui #define PTOMMAP(paddr) (paddr) 71447f87355Stsutsui #elif defined(__sparc64__) 71547f87355Stsutsui #define PTOMMAP(paddr) atop(paddr) 71647f87355Stsutsui #elif defined(__vax__) 71747f87355Stsutsui #define PTOMMAP(paddr) btop((u_int)paddr) 71847f87355Stsutsui #endif 71947f87355Stsutsui 72047f87355Stsutsui return PTOMMAP(paddr); 72147f87355Stsutsui } 72247f87355Stsutsui 72347f87355Stsutsui static int 72447f87355Stsutsui udl_alloc_screen(void *v, const struct wsscreen_descr *type, 72547f87355Stsutsui void **cookiep, int *curxp, int *curyp, long *attrp) 72647f87355Stsutsui { 72747f87355Stsutsui struct udl_softc *sc = v; 72847f87355Stsutsui 72947f87355Stsutsui if (sc->sc_nscreens > 0) 73047f87355Stsutsui return ENOMEM; 73147f87355Stsutsui 73247f87355Stsutsui /* 73347f87355Stsutsui * Initialize rasops. 73447f87355Stsutsui */ 73547f87355Stsutsui sc->sc_ri.ri_depth = sc->sc_depth; 73647f87355Stsutsui sc->sc_ri.ri_bits = NULL; 73747f87355Stsutsui sc->sc_ri.ri_width = sc->sc_width; 73847f87355Stsutsui sc->sc_ri.ri_height = sc->sc_height; 73947f87355Stsutsui sc->sc_ri.ri_stride = sc->sc_width * (sc->sc_depth / 8); 74047f87355Stsutsui sc->sc_ri.ri_hw = sc; 74147f87355Stsutsui sc->sc_ri.ri_flg = 0; 74247f87355Stsutsui 74347f87355Stsutsui if (sc->sc_depth == 16) { 74447f87355Stsutsui sc->sc_ri.ri_rnum = 5; 74547f87355Stsutsui sc->sc_ri.ri_gnum = 6; 74647f87355Stsutsui sc->sc_ri.ri_bnum = 5; 74747f87355Stsutsui sc->sc_ri.ri_rpos = 11; 74847f87355Stsutsui sc->sc_ri.ri_gpos = 5; 74947f87355Stsutsui sc->sc_ri.ri_bpos = 0; 75047f87355Stsutsui } 75147f87355Stsutsui 75247f87355Stsutsui rasops_init(&sc->sc_ri, sc->sc_height / 8, sc->sc_width / 8); 75347f87355Stsutsui 75447f87355Stsutsui sc->sc_ri.ri_ops.cursor = udl_cursor; 75547f87355Stsutsui sc->sc_ri.ri_ops.putchar = udl_putchar; 75647f87355Stsutsui sc->sc_ri.ri_ops.copycols = udl_copycols; 75747f87355Stsutsui sc->sc_ri.ri_ops.erasecols = udl_erasecols; 75847f87355Stsutsui sc->sc_ri.ri_ops.copyrows = udl_copyrows; 75947f87355Stsutsui sc->sc_ri.ri_ops.eraserows = udl_eraserows; 76047f87355Stsutsui 76147f87355Stsutsui sc->sc_ri.ri_ops.allocattr(&sc->sc_ri, 0, 0, 0, attrp); 76247f87355Stsutsui 76347f87355Stsutsui sc->sc_defaultscreen.ncols = sc->sc_ri.ri_cols; 76447f87355Stsutsui sc->sc_defaultscreen.nrows = sc->sc_ri.ri_rows; 76547f87355Stsutsui sc->sc_defaultscreen.textops = &sc->sc_ri.ri_ops; 76647f87355Stsutsui sc->sc_defaultscreen.fontwidth = sc->sc_ri.ri_font->fontwidth; 76747f87355Stsutsui sc->sc_defaultscreen.fontheight = sc->sc_ri.ri_font->fontheight; 76847f87355Stsutsui sc->sc_defaultscreen.capabilities = sc->sc_ri.ri_caps; 76947f87355Stsutsui 77047f87355Stsutsui *cookiep = &sc->sc_ri; 77147f87355Stsutsui *curxp = 0; 77247f87355Stsutsui *curyp = 0; 77347f87355Stsutsui 77447f87355Stsutsui sc->sc_nscreens++; 77547f87355Stsutsui 77647f87355Stsutsui return 0; 77747f87355Stsutsui } 77847f87355Stsutsui 77947f87355Stsutsui static void 78047f87355Stsutsui udl_free_screen(void *v, void *cookie) 78147f87355Stsutsui { 78247f87355Stsutsui struct udl_softc *sc = v; 78347f87355Stsutsui 78447f87355Stsutsui sc->sc_nscreens--; 78547f87355Stsutsui } 78647f87355Stsutsui 78747f87355Stsutsui static int 78847f87355Stsutsui udl_show_screen(void *v, void *cookie, int waitok, 78947f87355Stsutsui void (*cb)(void *, int, int), void *cbarg) 79047f87355Stsutsui { 79147f87355Stsutsui 79247f87355Stsutsui return 0; 79347f87355Stsutsui } 79447f87355Stsutsui 79547f87355Stsutsui static inline void 79647f87355Stsutsui udl_cmd_add_decomptable(struct udl_softc *sc, uint8_t *buf, int len) 79747f87355Stsutsui { 79847f87355Stsutsui 79947f87355Stsutsui udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_DECOMP); 80047f87355Stsutsui udl_cmd_add_4(sc, 0x263871cd); /* magic number */ 80147f87355Stsutsui udl_cmd_add_4(sc, 0x00000200); /* 512 byte chunks */ 80247f87355Stsutsui memcpy(sc->sc_cmd_buf, buf, len); 80347f87355Stsutsui sc->sc_cmd_buf += len; 80447f87355Stsutsui } 80547f87355Stsutsui 80647f87355Stsutsui static void 80747f87355Stsutsui udl_comp_load(struct udl_softc *sc) 80847f87355Stsutsui { 80947f87355Stsutsui struct udl_huffman *h; 81047f87355Stsutsui uint8_t *decomp; 81147f87355Stsutsui size_t decomp_size; 81247f87355Stsutsui int error, i; 81347f87355Stsutsui 81447f87355Stsutsui if (!(sc->sc_flags & UDL_DECOMPRDY)) { 81547f87355Stsutsui error = firmware_load("udl", "udl-decomp", &decomp, 81647f87355Stsutsui &decomp_size); 81747f87355Stsutsui if (error != 0) { 81847f87355Stsutsui aprint_error_dev(sc->sc_dev, 81947f87355Stsutsui "error %d, could not read decomp table %s!\n", 82047f87355Stsutsui error, "udl-decomp"); 82147f87355Stsutsui return; 82247f87355Stsutsui } 82347f87355Stsutsui udl_cmd_add_decomptable(sc, decomp, decomp_size); 82447f87355Stsutsui firmware_free(decomp, decomp_size); 82547f87355Stsutsui if (udl_cmd_send(sc) != 0) 82647f87355Stsutsui return; 82747f87355Stsutsui sc->sc_flags |= UDL_DECOMPRDY; 82847f87355Stsutsui } 82947f87355Stsutsui 83047f87355Stsutsui if (!(sc->sc_flags & UDL_COMPRDY)) { 83147f87355Stsutsui error = firmware_load("udl", "udl-comp", &sc->sc_huffman, 83247f87355Stsutsui &sc->sc_huffman_size); 83347f87355Stsutsui if (error != 0) { 83447f87355Stsutsui aprint_error_dev(sc->sc_dev, 83547f87355Stsutsui "error %d, could not read huffman table %s!\n", 83647f87355Stsutsui error, "udl-comp"); 83747f87355Stsutsui return; 83847f87355Stsutsui } 83947f87355Stsutsui h = (struct udl_huffman *)sc->sc_huffman; 84047f87355Stsutsui for (i = 0; i < UDL_HUFFMAN_RECORDS; i++) 84147f87355Stsutsui h[i].bit_pattern = be32toh(h[i].bit_pattern); 84247f87355Stsutsui sc->sc_huffman_base = sc->sc_huffman + UDL_HUFFMAN_BASE; 84347f87355Stsutsui sc->sc_flags |= UDL_COMPRDY; 84447f87355Stsutsui } 84547f87355Stsutsui } 84647f87355Stsutsui 84747f87355Stsutsui static void 84847f87355Stsutsui udl_comp_unload(struct udl_softc *sc) 84947f87355Stsutsui { 85047f87355Stsutsui 85147f87355Stsutsui if (sc->sc_flags & UDL_COMPRDY) { 85247f87355Stsutsui firmware_free(sc->sc_huffman, sc->sc_huffman_size); 85347f87355Stsutsui sc->sc_huffman = NULL; 85447f87355Stsutsui sc->sc_huffman_size = 0; 85547f87355Stsutsui sc->sc_flags &= ~UDL_COMPRDY; 85647f87355Stsutsui } 85747f87355Stsutsui } 85847f87355Stsutsui 85947f87355Stsutsui static int 86047f87355Stsutsui udl_fbmem_alloc(struct udl_softc *sc) 86147f87355Stsutsui { 86247f87355Stsutsui 8637b977f0dSnat mutex_enter(&sc->sc_thread_mtx); 864fd34ea77Schs if (sc->sc_fbmem == NULL) 8657b977f0dSnat sc->sc_fbmem = kmem_zalloc(UDL_FBMEM_SIZE(sc), KM_SLEEP); 866fd34ea77Schs if (sc->sc_fbmem_prev == NULL) 8677b977f0dSnat sc->sc_fbmem_prev = kmem_zalloc(UDL_FBMEM_SIZE(sc), KM_SLEEP); 8687b977f0dSnat mutex_exit(&sc->sc_thread_mtx); 86947f87355Stsutsui 87047f87355Stsutsui return 0; 87147f87355Stsutsui } 87247f87355Stsutsui 87347f87355Stsutsui static void 87447f87355Stsutsui udl_fbmem_free(struct udl_softc *sc) 87547f87355Stsutsui { 87647f87355Stsutsui 8777b977f0dSnat mutex_enter(&sc->sc_thread_mtx); 87847f87355Stsutsui if (sc->sc_fbmem != NULL) { 87947f87355Stsutsui kmem_free(sc->sc_fbmem, UDL_FBMEM_SIZE(sc)); 88047f87355Stsutsui sc->sc_fbmem = NULL; 88147f87355Stsutsui } 8827b977f0dSnat if (sc->sc_fbmem_prev != NULL) { 8837b977f0dSnat kmem_free(sc->sc_fbmem_prev, UDL_FBMEM_SIZE(sc)); 8847b977f0dSnat sc->sc_fbmem_prev = NULL; 8857b977f0dSnat } 8867b977f0dSnat mutex_exit(&sc->sc_thread_mtx); 88747f87355Stsutsui } 88847f87355Stsutsui 88947f87355Stsutsui static int 89047f87355Stsutsui udl_cmdq_alloc(struct udl_softc *sc) 89147f87355Stsutsui { 89247f87355Stsutsui struct udl_cmdq *cmdq; 89347f87355Stsutsui int i; 89447f87355Stsutsui 89547f87355Stsutsui TAILQ_INIT(&sc->sc_freecmd); 89647f87355Stsutsui TAILQ_INIT(&sc->sc_xfercmd); 89747f87355Stsutsui 89847f87355Stsutsui for (i = 0; i < UDL_NCMDQ; i++) { 89947f87355Stsutsui cmdq = &sc->sc_cmdq[i]; 90047f87355Stsutsui 90147f87355Stsutsui cmdq->cq_sc = sc; 90247f87355Stsutsui 9034e8e6643Sskrll int err = usbd_create_xfer(sc->sc_tx_pipeh, 9044e8e6643Sskrll UDL_CMD_BUFFER_SIZE, 0, 0, &cmdq->cq_xfer); 9054e8e6643Sskrll if (err) { 90647f87355Stsutsui aprint_error_dev(sc->sc_dev, 90747f87355Stsutsui "%s: can't allocate xfer handle!\n", __func__); 90847f87355Stsutsui goto error; 90947f87355Stsutsui } 91047f87355Stsutsui 9114e8e6643Sskrll cmdq->cq_buf = usbd_get_buffer(cmdq->cq_xfer); 91247f87355Stsutsui 91347f87355Stsutsui TAILQ_INSERT_TAIL(&sc->sc_freecmd, cmdq, cq_chain); 91447f87355Stsutsui } 91547f87355Stsutsui 91647f87355Stsutsui return 0; 91747f87355Stsutsui 91847f87355Stsutsui error: 91947f87355Stsutsui udl_cmdq_free(sc); 92047f87355Stsutsui return -1; 92147f87355Stsutsui } 92247f87355Stsutsui 92347f87355Stsutsui static void 92447f87355Stsutsui udl_cmdq_free(struct udl_softc *sc) 92547f87355Stsutsui { 92647f87355Stsutsui struct udl_cmdq *cmdq; 92747f87355Stsutsui int i; 92847f87355Stsutsui 92947f87355Stsutsui for (i = 0; i < UDL_NCMDQ; i++) { 93047f87355Stsutsui cmdq = &sc->sc_cmdq[i]; 93147f87355Stsutsui 93247f87355Stsutsui if (cmdq->cq_xfer != NULL) { 9334e8e6643Sskrll usbd_destroy_xfer(cmdq->cq_xfer); 93447f87355Stsutsui cmdq->cq_xfer = NULL; 93547f87355Stsutsui cmdq->cq_buf = NULL; 93647f87355Stsutsui } 93747f87355Stsutsui } 93847f87355Stsutsui } 93947f87355Stsutsui 94047f87355Stsutsui static struct udl_cmdq * 94147f87355Stsutsui udl_cmdq_get(struct udl_softc *sc) 94247f87355Stsutsui { 94347f87355Stsutsui struct udl_cmdq *cmdq; 94447f87355Stsutsui 94547f87355Stsutsui cmdq = TAILQ_FIRST(&sc->sc_freecmd); 94647f87355Stsutsui if (cmdq != NULL) { 94747f87355Stsutsui TAILQ_REMOVE(&sc->sc_freecmd, cmdq, cq_chain); 94847f87355Stsutsui UDL_EVCNT_INCR(&sc->sc_ev_cmdq_get); 94947f87355Stsutsui } 95047f87355Stsutsui 95147f87355Stsutsui return cmdq; 95247f87355Stsutsui } 95347f87355Stsutsui 95447f87355Stsutsui static void 95547f87355Stsutsui udl_cmdq_put(struct udl_softc *sc, struct udl_cmdq *cmdq) 95647f87355Stsutsui { 95747f87355Stsutsui 95847f87355Stsutsui TAILQ_INSERT_TAIL(&sc->sc_freecmd, cmdq, cq_chain); 95947f87355Stsutsui UDL_EVCNT_INCR(&sc->sc_ev_cmdq_put); 96047f87355Stsutsui } 96147f87355Stsutsui 96247f87355Stsutsui static void 96347f87355Stsutsui udl_cmdq_flush(struct udl_softc *sc) 96447f87355Stsutsui { 96547f87355Stsutsui 96647f87355Stsutsui mutex_enter(&sc->sc_mtx); 96747f87355Stsutsui while (TAILQ_FIRST(&sc->sc_xfercmd) != NULL) 96847f87355Stsutsui cv_wait(&sc->sc_cv, &sc->sc_mtx); 96947f87355Stsutsui mutex_exit(&sc->sc_mtx); 97047f87355Stsutsui } 97147f87355Stsutsui 97247f87355Stsutsui static void 97347f87355Stsutsui udl_cursor(void *cookie, int on, int row, int col) 97447f87355Stsutsui { 97547f87355Stsutsui struct rasops_info *ri = cookie; 97647f87355Stsutsui struct udl_softc *sc = ri->ri_hw; 97747f87355Stsutsui int x, y, width, height; 97847f87355Stsutsui 97947f87355Stsutsui if (ri->ri_flg & RI_CURSOR) 98047f87355Stsutsui udl_restore_char(ri); 98147f87355Stsutsui 98247f87355Stsutsui ri->ri_crow = row; 98347f87355Stsutsui ri->ri_ccol = col; 98447f87355Stsutsui 98547f87355Stsutsui if (on != 0) { 98647f87355Stsutsui ri->ri_flg |= RI_CURSOR; 98747f87355Stsutsui 98847f87355Stsutsui x = col * ri->ri_font->fontwidth; 98947f87355Stsutsui y = row * ri->ri_font->fontheight; 99047f87355Stsutsui width = ri->ri_font->fontwidth; 99147f87355Stsutsui height = ri->ri_font->fontheight; 99247f87355Stsutsui 99347f87355Stsutsui /* save the last character block to off-screen */ 99447f87355Stsutsui udl_copy_rect(sc, x, y, 0, sc->sc_offscreen, width, height); 99547f87355Stsutsui 99647f87355Stsutsui /* draw cursor */ 99747f87355Stsutsui udl_fill_rect(sc, 0xffff, x, y, width, 1); 99847f87355Stsutsui udl_fill_rect(sc, 0xffff, x, y + 1, 1, height - 2); 99947f87355Stsutsui udl_fill_rect(sc, 0xffff, x + width - 1, y + 1, 1, height - 2); 100047f87355Stsutsui udl_fill_rect(sc, 0xffff, x, y + height - 1, width, 1); 100147f87355Stsutsui 100247f87355Stsutsui udl_cmd_send_async(sc); 100347f87355Stsutsui } else 100447f87355Stsutsui ri->ri_flg &= ~RI_CURSOR; 100547f87355Stsutsui } 100647f87355Stsutsui 100747f87355Stsutsui static void 100847f87355Stsutsui udl_putchar(void *cookie, int row, int col, u_int uc, long attr) 100947f87355Stsutsui { 101047f87355Stsutsui struct rasops_info *ri = cookie; 101147f87355Stsutsui struct udl_softc *sc = ri->ri_hw; 101247f87355Stsutsui uint16_t rgb16[2]; 101347f87355Stsutsui int fg, bg, underline, x, y, width, height; 101447f87355Stsutsui 101547f87355Stsutsui rasops_unpack_attr(attr, &fg, &bg, &underline); 101647f87355Stsutsui rgb16[1] = (uint16_t)ri->ri_devcmap[fg]; 101747f87355Stsutsui rgb16[0] = (uint16_t)ri->ri_devcmap[bg]; 101847f87355Stsutsui 101947f87355Stsutsui x = col * ri->ri_font->fontwidth; 102047f87355Stsutsui y = row * ri->ri_font->fontheight; 102147f87355Stsutsui width = ri->ri_font->fontwidth; 102247f87355Stsutsui height = ri->ri_font->fontheight; 102347f87355Stsutsui 102447f87355Stsutsui if (uc == ' ') { 102547f87355Stsutsui /* 102647bd93c3Sandvar * Writing a block for the space character instead rendering 102747f87355Stsutsui * it from font bits is more slim. 102847f87355Stsutsui */ 102947f87355Stsutsui udl_fill_rect(sc, rgb16[0], x, y, width, height); 103047f87355Stsutsui } else { 103147f87355Stsutsui /* render a character from font bits */ 103247f87355Stsutsui udl_draw_char(ri, rgb16, uc, x, y); 103347f87355Stsutsui } 103447f87355Stsutsui 103547f87355Stsutsui if (underline != 0) 103647f87355Stsutsui udl_fill_rect(sc, rgb16[1], x, y + height - 1, width, 1); 103747f87355Stsutsui 103847f87355Stsutsui #if 0 103947f87355Stsutsui udl_cmd_send_async(sc); 104047f87355Stsutsui #endif 104147f87355Stsutsui } 104247f87355Stsutsui 104347f87355Stsutsui static void 104447f87355Stsutsui udl_copycols(void *cookie, int row, int src, int dst, int num) 104547f87355Stsutsui { 104647f87355Stsutsui struct rasops_info *ri = cookie; 104747f87355Stsutsui struct udl_softc *sc = ri->ri_hw; 104847f87355Stsutsui int sx, dx, y, width, height; 104947f87355Stsutsui 105047f87355Stsutsui sx = src * ri->ri_font->fontwidth; 105147f87355Stsutsui dx = dst * ri->ri_font->fontwidth; 105247f87355Stsutsui y = row * ri->ri_font->fontheight; 105347f87355Stsutsui width = num * ri->ri_font->fontwidth; 105447f87355Stsutsui height = ri->ri_font->fontheight; 105547f87355Stsutsui 105647f87355Stsutsui /* copy row block to off-screen first to fix overlay-copy problem */ 105747f87355Stsutsui udl_copy_rect(sc, sx, y, 0, sc->sc_offscreen, width, height); 105847f87355Stsutsui 105947f87355Stsutsui /* copy row block back from off-screen now */ 106047f87355Stsutsui udl_copy_rect(sc, 0, sc->sc_offscreen, dx, y, width, height); 106147f87355Stsutsui #if 0 106247f87355Stsutsui udl_cmd_send_async(sc); 106347f87355Stsutsui #endif 106447f87355Stsutsui } 106547f87355Stsutsui 106647f87355Stsutsui static void 106747f87355Stsutsui udl_erasecols(void *cookie, int row, int col, int num, long attr) 106847f87355Stsutsui { 106947f87355Stsutsui struct rasops_info *ri = cookie; 107047f87355Stsutsui struct udl_softc *sc = ri->ri_hw; 107147f87355Stsutsui uint16_t rgb16; 107247f87355Stsutsui int fg, bg, x, y, width, height; 107347f87355Stsutsui 107447f87355Stsutsui rasops_unpack_attr(attr, &fg, &bg, NULL); 107547f87355Stsutsui rgb16 = (uint16_t)ri->ri_devcmap[bg]; 107647f87355Stsutsui 107747f87355Stsutsui x = col * ri->ri_font->fontwidth; 107847f87355Stsutsui y = row * ri->ri_font->fontheight; 107947f87355Stsutsui width = num * ri->ri_font->fontwidth; 108047f87355Stsutsui height = ri->ri_font->fontheight; 108147f87355Stsutsui 108247f87355Stsutsui udl_fill_rect(sc, rgb16, x, y, width, height); 108347f87355Stsutsui #if 0 108447f87355Stsutsui udl_cmd_send_async(sc); 108547f87355Stsutsui #endif 108647f87355Stsutsui } 108747f87355Stsutsui 108847f87355Stsutsui static void 108947f87355Stsutsui udl_copyrows(void *cookie, int src, int dst, int num) 109047f87355Stsutsui { 109147f87355Stsutsui struct rasops_info *ri = cookie; 109247f87355Stsutsui struct udl_softc *sc = ri->ri_hw; 109347f87355Stsutsui int sy, ey, dy, width, height; 109447f87355Stsutsui 109547f87355Stsutsui width = ri->ri_emuwidth; 109647f87355Stsutsui height = ri->ri_font->fontheight; 109747f87355Stsutsui 109847f87355Stsutsui if (dst < src) { 109947f87355Stsutsui sy = src * height; 110047f87355Stsutsui ey = (src + num) * height; 110147f87355Stsutsui dy = dst * height; 110247f87355Stsutsui 110347f87355Stsutsui while (sy < ey) { 110447f87355Stsutsui udl_copy_rect(sc, 0, sy, 0, dy, width, height); 110547f87355Stsutsui sy += height; 110647f87355Stsutsui dy += height; 110747f87355Stsutsui } 110847f87355Stsutsui } else { 110947f87355Stsutsui sy = (src + num) * height; 111047f87355Stsutsui ey = src * height; 111147f87355Stsutsui dy = (dst + num) * height; 111247f87355Stsutsui 111347f87355Stsutsui while (sy > ey) { 111447f87355Stsutsui sy -= height; 111547f87355Stsutsui dy -= height; 111647f87355Stsutsui udl_copy_rect(sc, 0, sy, 0, dy, width, height); 111747f87355Stsutsui } 111847f87355Stsutsui } 111947f87355Stsutsui #if 0 112047f87355Stsutsui udl_cmd_send_async(sc); 112147f87355Stsutsui #endif 112247f87355Stsutsui } 112347f87355Stsutsui 112447f87355Stsutsui static void 112547f87355Stsutsui udl_eraserows(void *cookie, int row, int num, long attr) 112647f87355Stsutsui { 112747f87355Stsutsui struct rasops_info *ri = cookie; 112847f87355Stsutsui struct udl_softc *sc = ri->ri_hw; 112947f87355Stsutsui uint16_t rgb16; 113047f87355Stsutsui int fg, bg, y, width, height; 113147f87355Stsutsui 113247f87355Stsutsui rasops_unpack_attr(attr, &fg, &bg, NULL); 113347f87355Stsutsui rgb16 = (uint16_t)ri->ri_devcmap[bg]; 113447f87355Stsutsui 113547f87355Stsutsui y = row * ri->ri_font->fontheight; 113647f87355Stsutsui width = ri->ri_emuwidth; 113747f87355Stsutsui height = num * ri->ri_font->fontheight; 113847f87355Stsutsui 113947f87355Stsutsui udl_fill_rect(sc, rgb16, 0, y, width, height); 114047f87355Stsutsui #if 0 114147f87355Stsutsui udl_cmd_send_async(sc); 114247f87355Stsutsui #endif 114347f87355Stsutsui } 114447f87355Stsutsui 114547f87355Stsutsui static void 114647f87355Stsutsui udl_restore_char(struct rasops_info *ri) 114747f87355Stsutsui { 114847f87355Stsutsui struct udl_softc *sc = ri->ri_hw; 114947f87355Stsutsui int x, y, width, height; 115047f87355Stsutsui 115147f87355Stsutsui x = ri->ri_ccol * ri->ri_font->fontwidth; 115247f87355Stsutsui y = ri->ri_crow * ri->ri_font->fontheight; 115347f87355Stsutsui width = ri->ri_font->fontwidth; 115447f87355Stsutsui height = ri->ri_font->fontheight; 115547f87355Stsutsui 115647f87355Stsutsui /* restore the last saved character from off-screen */ 115747f87355Stsutsui udl_copy_rect(sc, 0, sc->sc_offscreen, x, y, width, height); 115847f87355Stsutsui } 115947f87355Stsutsui 116047f87355Stsutsui static void 116147f87355Stsutsui udl_draw_char(struct rasops_info *ri, uint16_t *rgb16, u_int uc, int x, int y) 116247f87355Stsutsui { 116347f87355Stsutsui struct udl_softc *sc = ri->ri_hw; 116447f87355Stsutsui struct wsdisplay_font *font = ri->ri_font; 116547f87355Stsutsui uint32_t fontbits; 116647f87355Stsutsui uint16_t pixels[32]; 116747f87355Stsutsui uint8_t *fontbase; 116847f87355Stsutsui int i, soff, eoff; 116947f87355Stsutsui 117047f87355Stsutsui soff = y * sc->sc_width + x; 117147f87355Stsutsui eoff = (y + font->fontheight) * sc->sc_width + x; 117247f87355Stsutsui fontbase = (uint8_t *)font->data + (uc - font->firstchar) * 117347f87355Stsutsui ri->ri_fontscale; 117447f87355Stsutsui 117547f87355Stsutsui while (soff < eoff) { 117647f87355Stsutsui fontbits = 0; 117747f87355Stsutsui switch (font->stride) { 117847f87355Stsutsui case 4: 117947f87355Stsutsui fontbits |= fontbase[3]; 118047f87355Stsutsui /* FALLTHROUGH */ 118147f87355Stsutsui case 3: 118247f87355Stsutsui fontbits |= fontbase[2] << 8; 118347f87355Stsutsui /* FALLTHROUGH */ 118447f87355Stsutsui case 2: 118547f87355Stsutsui fontbits |= fontbase[1] << 16; 118647f87355Stsutsui /* FALLTHROUGH */ 118747f87355Stsutsui case 1: 118847f87355Stsutsui fontbits |= fontbase[0] << 24; 118947f87355Stsutsui } 119047f87355Stsutsui fontbase += font->stride; 119147f87355Stsutsui 119247f87355Stsutsui for (i = 0; i < font->fontwidth; i++) { 119347f87355Stsutsui pixels[i] = rgb16[(fontbits >> 31) & 1]; 119447f87355Stsutsui fontbits <<= 1; 119547f87355Stsutsui } 119647f87355Stsutsui 119747f87355Stsutsui udl_draw_line(sc, pixels, soff, font->fontwidth); 119847f87355Stsutsui soff += sc->sc_width; 119947f87355Stsutsui } 120047f87355Stsutsui } 120147f87355Stsutsui 120247f87355Stsutsui static void 120347f87355Stsutsui udl_copy_rect(struct udl_softc *sc, int sx, int sy, int dx, int dy, int width, 120447f87355Stsutsui int height) 120547f87355Stsutsui { 120647f87355Stsutsui int sbase, soff, ebase, eoff, dbase, doff, width_cur; 120747f87355Stsutsui 120847f87355Stsutsui sbase = sy * sc->sc_width; 120947f87355Stsutsui ebase = (sy + height) * sc->sc_width; 121047f87355Stsutsui dbase = dy * sc->sc_width; 121147f87355Stsutsui 121247f87355Stsutsui while (width > 0) { 121347f87355Stsutsui soff = sbase + sx; 121447f87355Stsutsui eoff = ebase + sx; 121547f87355Stsutsui doff = dbase + dx; 121647f87355Stsutsui 121747f87355Stsutsui if (width >= UDL_CMD_WIDTH_MAX) 121847f87355Stsutsui width_cur = UDL_CMD_WIDTH_MAX; 121947f87355Stsutsui else 122047f87355Stsutsui width_cur = width; 122147f87355Stsutsui 122247f87355Stsutsui while (soff < eoff) { 122347f87355Stsutsui udl_copy_line(sc, soff, doff, width_cur); 122447f87355Stsutsui soff += sc->sc_width; 122547f87355Stsutsui doff += sc->sc_width; 122647f87355Stsutsui } 122747f87355Stsutsui 122847f87355Stsutsui sx += width_cur; 122947f87355Stsutsui dx += width_cur; 123047f87355Stsutsui width -= width_cur; 123147f87355Stsutsui } 123247f87355Stsutsui } 123347f87355Stsutsui 123447f87355Stsutsui static void 123547f87355Stsutsui udl_fill_rect(struct udl_softc *sc, uint16_t rgb16, int x, int y, int width, 123647f87355Stsutsui int height) 123747f87355Stsutsui { 123847f87355Stsutsui int sbase, soff, ebase, eoff, width_cur; 123947f87355Stsutsui 124047f87355Stsutsui sbase = y * sc->sc_width; 124147f87355Stsutsui ebase = (y + height) * sc->sc_width; 124247f87355Stsutsui 124347f87355Stsutsui while (width > 0) { 124447f87355Stsutsui soff = sbase + x; 124547f87355Stsutsui eoff = ebase + x; 124647f87355Stsutsui 124747f87355Stsutsui if (width >= UDL_CMD_WIDTH_MAX) 124847f87355Stsutsui width_cur = UDL_CMD_WIDTH_MAX; 124947f87355Stsutsui else 125047f87355Stsutsui width_cur = width; 125147f87355Stsutsui 125247f87355Stsutsui while (soff < eoff) { 125347f87355Stsutsui udl_fill_line(sc, rgb16, soff, width_cur); 125447f87355Stsutsui soff += sc->sc_width; 125547f87355Stsutsui } 125647f87355Stsutsui 125747f87355Stsutsui x += width_cur; 125847f87355Stsutsui width -= width_cur; 125947f87355Stsutsui } 126047f87355Stsutsui } 126147f87355Stsutsui 126247f87355Stsutsui #ifdef notyet 126347f87355Stsutsui static void 126447f87355Stsutsui udl_draw_rect(struct udl_softc *sc, struct udl_ioctl_damage *d) 126547f87355Stsutsui { 126647f87355Stsutsui int sbase, soff, ebase, eoff, x, y, width, width_cur, height; 126747f87355Stsutsui 126847f87355Stsutsui x = d->x1; 126947f87355Stsutsui y = d->y1; 12705401ccabSkiyohara width = d->x2 - d->x1; 12715401ccabSkiyohara height = d->y2 - d->y1; 127247f87355Stsutsui sbase = y * sc->sc_width; 127347f87355Stsutsui ebase = (y + height) * sc->sc_width; 127447f87355Stsutsui 127547f87355Stsutsui while (width > 0) { 127647f87355Stsutsui soff = sbase + x; 127747f87355Stsutsui eoff = ebase + x; 127847f87355Stsutsui 127947f87355Stsutsui if (width >= UDL_CMD_WIDTH_MAX) 128047f87355Stsutsui width_cur = UDL_CMD_WIDTH_MAX; 128147f87355Stsutsui else 128247f87355Stsutsui width_cur = width; 128347f87355Stsutsui 128447f87355Stsutsui while (soff < eoff) { 128547f87355Stsutsui udl_draw_line(sc, (uint16_t *)sc->sc_fbmem + soff, 128647f87355Stsutsui soff, width_cur); 128747f87355Stsutsui soff += sc->sc_width; 128847f87355Stsutsui } 128947f87355Stsutsui 129047f87355Stsutsui x += width_cur; 129147f87355Stsutsui width -= width_cur; 129247f87355Stsutsui } 129347f87355Stsutsui 129447f87355Stsutsui udl_cmd_send_async(sc); 129547f87355Stsutsui } 129647f87355Stsutsui 129747f87355Stsutsui static void 129847f87355Stsutsui udl_draw_rect_comp(struct udl_softc *sc, struct udl_ioctl_damage *d) 129947f87355Stsutsui { 130047f87355Stsutsui int soff, eoff, x, y, width, height; 130147f87355Stsutsui 130247f87355Stsutsui x = d->x1; 130347f87355Stsutsui y = d->y1; 13045401ccabSkiyohara width = d->x2 - d->x1; 13055401ccabSkiyohara height = d->y2 - d->y1; 130647f87355Stsutsui soff = y * sc->sc_width + x; 130747f87355Stsutsui eoff = (y + height) * sc->sc_width + x; 130847f87355Stsutsui 130947f87355Stsutsui udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 131047f87355Stsutsui sc->sc_cmd_cblen = 4; 131147f87355Stsutsui 131247f87355Stsutsui while (soff < eoff) { 131347f87355Stsutsui udl_draw_line_comp(sc, (uint16_t *)sc->sc_fbmem + soff, soff, 131447f87355Stsutsui width); 131547f87355Stsutsui soff += sc->sc_width; 131647f87355Stsutsui } 131747f87355Stsutsui 131847f87355Stsutsui udl_cmd_send_async(sc); 131947f87355Stsutsui } 132047f87355Stsutsui #endif 132147f87355Stsutsui 132247f87355Stsutsui static inline void 132347f87355Stsutsui udl_copy_line(struct udl_softc *sc, int soff, int doff, int width) 132447f87355Stsutsui { 132547f87355Stsutsui 132647f87355Stsutsui if (__predict_false((UDL_CMD_BUFSIZE(sc) + UDL_CMD_COPY_SIZE + 2) > 132747f87355Stsutsui UDL_CMD_BUFFER_SIZE)) 132847f87355Stsutsui udl_cmd_send_async(sc); 132947f87355Stsutsui 133047f87355Stsutsui udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_COPY16); 133147f87355Stsutsui udl_cmd_add_4(sc, ((doff * 2) << 8) | (width & 0xff)); 133247f87355Stsutsui 133347f87355Stsutsui udl_cmd_add_3(sc, soff * 2); 133447f87355Stsutsui } 133547f87355Stsutsui 133647f87355Stsutsui static inline void 133747f87355Stsutsui udl_fill_line(struct udl_softc *sc, uint16_t rgb16, int off, int width) 133847f87355Stsutsui { 133947f87355Stsutsui 134047f87355Stsutsui if (__predict_false((UDL_CMD_BUFSIZE(sc) + UDL_CMD_FILL_SIZE + 2) > 134147f87355Stsutsui UDL_CMD_BUFFER_SIZE)) 134247f87355Stsutsui udl_cmd_send_async(sc); 134347f87355Stsutsui 134447f87355Stsutsui udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_RLE16); 134547f87355Stsutsui udl_cmd_add_4(sc, ((off * 2) << 8) | (width & 0xff)); 134647f87355Stsutsui 134747f87355Stsutsui udl_cmd_add_1(sc, width); 134847f87355Stsutsui udl_cmd_add_2(sc, rgb16); 134947f87355Stsutsui } 135047f87355Stsutsui 135147f87355Stsutsui static inline void 135247f87355Stsutsui udl_draw_line(struct udl_softc *sc, uint16_t *buf, int off, int width) 135347f87355Stsutsui { 135447f87355Stsutsui 135547f87355Stsutsui if (__predict_false( 135647f87355Stsutsui (UDL_CMD_BUFSIZE(sc) + UDL_CMD_DRAW_SIZE(width) + 2) > 135747f87355Stsutsui UDL_CMD_BUFFER_SIZE)) 135847f87355Stsutsui udl_cmd_send_async(sc); 135947f87355Stsutsui 136047f87355Stsutsui udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_WRITE16); 136147f87355Stsutsui udl_cmd_add_4(sc, ((off * 2) << 8) | (width & 0xff)); 136247f87355Stsutsui 136347f87355Stsutsui udl_cmd_add_buf(sc, buf, width); 136447f87355Stsutsui } 136547f87355Stsutsui 1366d81879ebSjoerg #ifdef notyet 136747f87355Stsutsui static inline int 136847f87355Stsutsui udl_cmd_add_buf_comp(struct udl_softc *sc, uint16_t *buf, int width) 136947f87355Stsutsui { 137047f87355Stsutsui struct udl_huffman *h; 137147f87355Stsutsui uint16_t *startp, *endp; 137247f87355Stsutsui uint32_t bit_pattern; 137347f87355Stsutsui uint16_t prev; 137447f87355Stsutsui int16_t diff; 137547f87355Stsutsui uint8_t bit_count, bit_pos, bit_rem, curlen; 137647f87355Stsutsui 137747f87355Stsutsui startp = buf; 137847f87355Stsutsui if (width >= UDL_CMD_WIDTH_MAX) 137947f87355Stsutsui endp = buf + UDL_CMD_WIDTH_MAX; 138047f87355Stsutsui else 138147f87355Stsutsui endp = buf + width; 138247f87355Stsutsui 138347f87355Stsutsui prev = bit_pos = *sc->sc_cmd_buf = 0; 138447f87355Stsutsui bit_rem = 8; 138547f87355Stsutsui 138647f87355Stsutsui /* 138747f87355Stsutsui * Generate a sub-block with maximal 256 pixels compressed data. 138847f87355Stsutsui */ 138947f87355Stsutsui while (buf < endp) { 139047f87355Stsutsui /* get difference between current and previous pixel */ 139147f87355Stsutsui diff = *buf - prev; 139247f87355Stsutsui 139347f87355Stsutsui /* get the huffman difference bit sequence */ 139447f87355Stsutsui h = (struct udl_huffman *)sc->sc_huffman_base + diff; 139547f87355Stsutsui bit_count = h->bit_count; 139647f87355Stsutsui bit_pattern = h->bit_pattern; 139747f87355Stsutsui 139847f87355Stsutsui curlen = (bit_pos + bit_count + 7) / 8; 139947f87355Stsutsui if (__predict_false((sc->sc_cmd_cblen + curlen + 1) > 140047f87355Stsutsui UDL_CMD_COMP_BLOCK_SIZE)) 140147f87355Stsutsui break; 140247f87355Stsutsui 140347f87355Stsutsui /* generate one pixel compressed data */ 140447f87355Stsutsui while (bit_count >= bit_rem) { 140547f87355Stsutsui *sc->sc_cmd_buf++ |= 140647f87355Stsutsui (bit_pattern & ((1 << bit_rem) - 1)) << bit_pos; 140747f87355Stsutsui *sc->sc_cmd_buf = 0; 140847f87355Stsutsui sc->sc_cmd_cblen++; 140947f87355Stsutsui bit_count -= bit_rem; 141047f87355Stsutsui bit_pattern >>= bit_rem; 141147f87355Stsutsui bit_pos = 0; 141247f87355Stsutsui bit_rem = 8; 141347f87355Stsutsui } 141447f87355Stsutsui 141547f87355Stsutsui if (bit_count > 0) { 141647f87355Stsutsui *sc->sc_cmd_buf |= 141747f87355Stsutsui (bit_pattern & ((1 << bit_count) - 1)) << bit_pos; 141847f87355Stsutsui bit_pos += bit_count; 141947f87355Stsutsui bit_rem -= bit_count; 142047f87355Stsutsui } 142147f87355Stsutsui 142247f87355Stsutsui prev = *buf++; 142347f87355Stsutsui } 142447f87355Stsutsui 142547f87355Stsutsui /* 142647f87355Stsutsui * If we have bits left in our last byte, round up to the next 142747f87355Stsutsui * byte, so we don't overwrite them. 142847f87355Stsutsui */ 142947f87355Stsutsui if (bit_pos > 0) { 143047f87355Stsutsui sc->sc_cmd_buf++; 143147f87355Stsutsui sc->sc_cmd_cblen++; 143247f87355Stsutsui } 143347f87355Stsutsui 143447f87355Stsutsui /* return how many pixels we have compressed */ 143547f87355Stsutsui return buf - startp; 143647f87355Stsutsui } 143747f87355Stsutsui 143847f87355Stsutsui static inline void 143947f87355Stsutsui udl_draw_line_comp(struct udl_softc *sc, uint16_t *buf, int off, int width) 144047f87355Stsutsui { 144147f87355Stsutsui uint8_t *widthp; 144247f87355Stsutsui int width_cur; 144347f87355Stsutsui 144447f87355Stsutsui while (width > 0) { 144547f87355Stsutsui if (__predict_false( 144647f87355Stsutsui (sc->sc_cmd_cblen + UDL_CMD_COMP_MIN_SIZE + 1) > 144747f87355Stsutsui UDL_CMD_COMP_BLOCK_SIZE)) { 144847f87355Stsutsui if (UDL_CMD_BUFSIZE(sc) < UDL_CMD_COMP_THRESHOLD) { 144947f87355Stsutsui while (sc->sc_cmd_cblen < 145047f87355Stsutsui UDL_CMD_COMP_BLOCK_SIZE) { 145147f87355Stsutsui *sc->sc_cmd_buf++ = 0; 145247f87355Stsutsui sc->sc_cmd_cblen++; 145347f87355Stsutsui } 145447f87355Stsutsui } else 145547f87355Stsutsui udl_cmd_send_async(sc); 145647f87355Stsutsui udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 145747f87355Stsutsui sc->sc_cmd_cblen = 4; 145847f87355Stsutsui } 145947f87355Stsutsui 146047f87355Stsutsui udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | 146147f87355Stsutsui (UDL_BULK_CMD_FB_WRITE16 | UDL_BULK_CMD_FB_COMP)); 146247f87355Stsutsui udl_cmd_add_4(sc, (off * 2) << 8); 146347f87355Stsutsui 146447f87355Stsutsui widthp = sc->sc_cmd_buf - 1; 146547f87355Stsutsui 146647f87355Stsutsui sc->sc_cmd_cblen += UDL_CMD_HEADER_SIZE; 146747f87355Stsutsui 146847f87355Stsutsui width_cur = udl_cmd_add_buf_comp(sc, buf, width); 146947f87355Stsutsui 147047f87355Stsutsui *widthp = width_cur; 147147f87355Stsutsui buf += width_cur; 147247f87355Stsutsui off += width_cur; 147347f87355Stsutsui width -= width_cur; 147447f87355Stsutsui } 147547f87355Stsutsui } 1476d81879ebSjoerg #endif 147747f87355Stsutsui 147847f87355Stsutsui static int 147947f87355Stsutsui udl_cmd_send(struct udl_softc *sc) 148047f87355Stsutsui { 148147f87355Stsutsui struct udl_cmdq *cmdq; 148247f87355Stsutsui usbd_status error; 148347f87355Stsutsui uint32_t len; 148447f87355Stsutsui 148547f87355Stsutsui cmdq = sc->sc_cmd_cur; 148647f87355Stsutsui 148747f87355Stsutsui /* mark end of command stack */ 148847f87355Stsutsui udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_EOC); 148947f87355Stsutsui 149047f87355Stsutsui len = UDL_CMD_BUFSIZE(sc); 149147f87355Stsutsui 149247f87355Stsutsui /* do xfer */ 14934e8e6643Sskrll error = usbd_bulk_transfer(cmdq->cq_xfer, sc->sc_tx_pipeh, 0, 14944e8e6643Sskrll USBD_NO_TIMEOUT, cmdq->cq_buf, &len); 149547f87355Stsutsui 149647f87355Stsutsui UDL_CMD_BUFINIT(sc); 149747f87355Stsutsui 149847f87355Stsutsui if (error != USBD_NORMAL_COMPLETION) { 149947f87355Stsutsui aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__, 150047f87355Stsutsui usbd_errstr(error)); 150147f87355Stsutsui return -1; 150247f87355Stsutsui } 150347f87355Stsutsui 150447f87355Stsutsui return 0; 150547f87355Stsutsui } 150647f87355Stsutsui 150747f87355Stsutsui static void 150847f87355Stsutsui udl_cmd_send_async(struct udl_softc *sc) 150947f87355Stsutsui { 151047f87355Stsutsui struct udl_cmdq *cmdq; 151147f87355Stsutsui usbd_status error; 151247f87355Stsutsui uint32_t len; 151347f87355Stsutsui 151447f87355Stsutsui #if 1 151547f87355Stsutsui /* 151647f87355Stsutsui * XXX 151747f87355Stsutsui * All tty ops for wsemul are called with tty_lock spin mutex held, 151847f87355Stsutsui * so we can't call cv_wait(9) here to acquire a free buffer. 151947f87355Stsutsui * For now, all commands and data for wsemul ops are discarded 152047f87355Stsutsui * if there is no free command buffer, and then screen text might 152147f87355Stsutsui * be corrupted on large scroll ops etc. 152247f87355Stsutsui * 152347f87355Stsutsui * Probably we have to reorganize the giant tty_lock mutex, or 152447f87355Stsutsui * change wsdisplay APIs (especially wsdisplaystart()) to return 152547f87355Stsutsui * a number of actually handled characters as OpenBSD does, but 152647f87355Stsutsui * the latter one requires whole API changes around rasops(9) etc. 152747f87355Stsutsui */ 152847f87355Stsutsui if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) { 152947f87355Stsutsui if (TAILQ_FIRST(&sc->sc_freecmd) == NULL) { 153047f87355Stsutsui UDL_CMD_BUFINIT(sc); 153147f87355Stsutsui return; 153247f87355Stsutsui } 153347f87355Stsutsui } 153447f87355Stsutsui #endif 153547f87355Stsutsui 153647f87355Stsutsui cmdq = sc->sc_cmd_cur; 153747f87355Stsutsui 153847f87355Stsutsui /* mark end of command stack */ 153947f87355Stsutsui udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_EOC); 154047f87355Stsutsui 154147f87355Stsutsui len = UDL_CMD_BUFSIZE(sc); 154247f87355Stsutsui 154347f87355Stsutsui /* do xfer */ 154447f87355Stsutsui mutex_enter(&sc->sc_mtx); 15454e8e6643Sskrll usbd_setup_xfer(cmdq->cq_xfer, cmdq, cmdq->cq_buf, 15464e8e6643Sskrll len, 0, USBD_NO_TIMEOUT, udl_cmd_send_async_cb); 15478da94dbcSnat mutex_exit(&sc->sc_mtx); 154847f87355Stsutsui error = usbd_transfer(cmdq->cq_xfer); 15498da94dbcSnat mutex_enter(&sc->sc_mtx); 155047f87355Stsutsui if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) { 155147f87355Stsutsui aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__, 155247f87355Stsutsui usbd_errstr(error)); 155347f87355Stsutsui mutex_exit(&sc->sc_mtx); 155447f87355Stsutsui goto end; 155547f87355Stsutsui } 155647f87355Stsutsui 155747f87355Stsutsui TAILQ_INSERT_TAIL(&sc->sc_xfercmd, cmdq, cq_chain); 155847f87355Stsutsui cmdq = udl_cmdq_get(sc); 155947f87355Stsutsui mutex_exit(&sc->sc_mtx); 156047f87355Stsutsui while (cmdq == NULL) { 156147f87355Stsutsui int err; 156247f87355Stsutsui UDL_EVCNT_INCR(&sc->sc_ev_cmdq_wait); 156347f87355Stsutsui mutex_enter(&sc->sc_mtx); 156447f87355Stsutsui err = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, 156547f87355Stsutsui mstohz(100) /* XXX is this needed? */); 156647f87355Stsutsui if (err != 0) { 156747f87355Stsutsui DPRINTF(("%s: %s: cv timeout (error = %d)\n", 156847f87355Stsutsui device_xname(sc->sc_dev), __func__, err)); 156947f87355Stsutsui UDL_EVCNT_INCR(&sc->sc_ev_cmdq_timeout); 157047f87355Stsutsui } 157147f87355Stsutsui cmdq = udl_cmdq_get(sc); 157247f87355Stsutsui mutex_exit(&sc->sc_mtx); 157347f87355Stsutsui } 157447f87355Stsutsui sc->sc_cmd_cur = cmdq; 157547f87355Stsutsui end: 157647f87355Stsutsui UDL_CMD_BUFINIT(sc); 157747f87355Stsutsui } 157847f87355Stsutsui 157947f87355Stsutsui static void 15804e8e6643Sskrll udl_cmd_send_async_cb(struct usbd_xfer *xfer, void * priv, 158147f87355Stsutsui usbd_status status) 158247f87355Stsutsui { 158347f87355Stsutsui struct udl_cmdq *cmdq = priv; 158447f87355Stsutsui struct udl_softc *sc = cmdq->cq_sc; 158547f87355Stsutsui 158647f87355Stsutsui if (status != USBD_NORMAL_COMPLETION) { 158747f87355Stsutsui aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__, 158847f87355Stsutsui usbd_errstr(status)); 158947f87355Stsutsui 159047f87355Stsutsui if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) 159147f87355Stsutsui return; 159247f87355Stsutsui if (status == USBD_STALLED) 159347f87355Stsutsui usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh); 159447f87355Stsutsui } 159547f87355Stsutsui 159647f87355Stsutsui mutex_enter(&sc->sc_mtx); 159747f87355Stsutsui TAILQ_REMOVE(&sc->sc_xfercmd, cmdq, cq_chain); 159847f87355Stsutsui udl_cmdq_put(sc, cmdq); 159947f87355Stsutsui 1600eaf4153fSskrll /* signal xfer op that sleeps for a free xfer buffer */ 160147f87355Stsutsui cv_signal(&sc->sc_cv); 160247f87355Stsutsui mutex_exit(&sc->sc_mtx); 160347f87355Stsutsui } 160447f87355Stsutsui 160547f87355Stsutsui static int 160647f87355Stsutsui udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r, uint16_t index, 160747f87355Stsutsui uint16_t value, uint8_t *buf, uint16_t len) 160847f87355Stsutsui { 160947f87355Stsutsui usb_device_request_t req; 161047f87355Stsutsui usbd_status error; 161147f87355Stsutsui 161247f87355Stsutsui req.bmRequestType = rt; 161347f87355Stsutsui req.bRequest = r; 161447f87355Stsutsui USETW(req.wIndex, index); 161547f87355Stsutsui USETW(req.wValue, value); 161647f87355Stsutsui USETW(req.wLength, len); 161747f87355Stsutsui 161847f87355Stsutsui error = usbd_do_request(sc->sc_udev, &req, buf); 161947f87355Stsutsui if (error != USBD_NORMAL_COMPLETION) { 162047f87355Stsutsui aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__, 162147f87355Stsutsui usbd_errstr(error)); 162247f87355Stsutsui return -1; 162347f87355Stsutsui } 162447f87355Stsutsui 162547f87355Stsutsui return 0; 162647f87355Stsutsui } 162747f87355Stsutsui 162847f87355Stsutsui static int 162947f87355Stsutsui udl_init(struct udl_softc *sc) 163047f87355Stsutsui { 163147f87355Stsutsui static uint8_t key[16] = { 163247f87355Stsutsui 0x57, 0xcd, 0xdc, 0xa7, 0x1c, 0x88, 0x5e, 0x15, 163347f87355Stsutsui 0x60, 0xfe, 0xc6, 0x97, 0x16, 0x3d, 0x47, 0xf2 163447f87355Stsutsui }; 163547f87355Stsutsui uint8_t status[4], val; 163647f87355Stsutsui 163747f87355Stsutsui if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_STATUS, 163847f87355Stsutsui 0x0000, 0x0000, status, sizeof(status)) != 0) 163947f87355Stsutsui return -1; 164047f87355Stsutsui 164147f87355Stsutsui if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_1, 164247f87355Stsutsui 0xc484, 0x0000, &val, 1) != 0) 164347f87355Stsutsui return -1; 164447f87355Stsutsui 164547f87355Stsutsui val = 1; 164647f87355Stsutsui if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_WRITE_1, 164747f87355Stsutsui 0xc41f, 0x0000, &val, 1) != 0) 164847f87355Stsutsui return -1; 164947f87355Stsutsui 165047f87355Stsutsui if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_SET_KEY, 165147f87355Stsutsui 0x0000, 0x0000, key, sizeof(key)) != 0) 165247f87355Stsutsui return -1; 165347f87355Stsutsui 165447f87355Stsutsui val = 0; 165547f87355Stsutsui if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_WRITE_1, 165647f87355Stsutsui 0xc40b, 0x0000, &val, 1) != 0) 165747f87355Stsutsui return -1; 165847f87355Stsutsui 165947f87355Stsutsui return 0; 166047f87355Stsutsui } 166147f87355Stsutsui 166247f87355Stsutsui static void 166347f87355Stsutsui udl_read_edid(struct udl_softc *sc) 166447f87355Stsutsui { 166547f87355Stsutsui uint8_t buf[64], edid[128]; 166647f87355Stsutsui int offset; 166747f87355Stsutsui 166847f87355Stsutsui memset(&sc->sc_ei, 0, sizeof(struct edid_info)); 166947f87355Stsutsui 167047f87355Stsutsui offset = 0; 167147f87355Stsutsui if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID, 167247f87355Stsutsui 0x00a1, (offset << 8), buf, 64) != 0) 167347f87355Stsutsui return; 167447f87355Stsutsui if (buf[0] != 0) 167547f87355Stsutsui return; 167647f87355Stsutsui memcpy(&edid[offset], &buf[1], 63); 167747f87355Stsutsui offset += 63; 167847f87355Stsutsui 167947f87355Stsutsui if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID, 168047f87355Stsutsui 0x00a1, (offset << 8), buf, 64) != 0) 168147f87355Stsutsui return; 168247f87355Stsutsui if (buf[0] != 0) 168347f87355Stsutsui return; 168447f87355Stsutsui memcpy(&edid[offset], &buf[1], 63); 168547f87355Stsutsui offset += 63; 168647f87355Stsutsui 168747f87355Stsutsui if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID, 168847f87355Stsutsui 0x00a1, (offset << 8), buf, 3) != 0) 168947f87355Stsutsui return; 169047f87355Stsutsui if (buf[0] != 0) 169147f87355Stsutsui return; 169247f87355Stsutsui memcpy(&edid[offset], &buf[1], 2); 169347f87355Stsutsui 169447f87355Stsutsui if (edid_parse(edid, &sc->sc_ei) == 0) { 169547f87355Stsutsui #ifdef UDL_DEBUG 169647f87355Stsutsui edid_print(&sc->sc_ei); 169747f87355Stsutsui #endif 169847f87355Stsutsui } 169947f87355Stsutsui } 170047f87355Stsutsui 170147f87355Stsutsui static void 170247f87355Stsutsui udl_set_address(struct udl_softc *sc, int start16, int stride16, int start8, 170347f87355Stsutsui int stride8) 170447f87355Stsutsui { 170547f87355Stsutsui udl_reg_write_1(sc, UDL_REG_SYNC, 0x00); 170647f87355Stsutsui udl_reg_write_3(sc, UDL_REG_ADDR_START16, start16); 170747f87355Stsutsui udl_reg_write_3(sc, UDL_REG_ADDR_STRIDE16, stride16); 170847f87355Stsutsui udl_reg_write_3(sc, UDL_REG_ADDR_START8, start8); 170947f87355Stsutsui udl_reg_write_3(sc, UDL_REG_ADDR_STRIDE8, stride8); 171047f87355Stsutsui udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 171147f87355Stsutsui } 171247f87355Stsutsui 171347f87355Stsutsui static void 171447f87355Stsutsui udl_blank(struct udl_softc *sc, int blank) 171547f87355Stsutsui { 171647f87355Stsutsui 171747f87355Stsutsui if (blank != 0) 171847f87355Stsutsui udl_reg_write_1(sc, UDL_REG_BLANK, UDL_REG_BLANK_ON); 171947f87355Stsutsui else 172047f87355Stsutsui udl_reg_write_1(sc, UDL_REG_BLANK, UDL_REG_BLANK_OFF); 172147f87355Stsutsui udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 172247f87355Stsutsui } 172347f87355Stsutsui 172447f87355Stsutsui static uint16_t 172547f87355Stsutsui udl_lfsr(uint16_t count) 172647f87355Stsutsui { 172747f87355Stsutsui uint16_t val = 0xffff; 172847f87355Stsutsui 172947f87355Stsutsui while (count > 0) { 173047f87355Stsutsui val = (uint16_t)(val << 1) | ((uint16_t)( 173147f87355Stsutsui (uint16_t)(val << 0) ^ 173247f87355Stsutsui (uint16_t)(val << 11) ^ 173347f87355Stsutsui (uint16_t)(val << 13) ^ 173447f87355Stsutsui (uint16_t)(val << 14) 173547f87355Stsutsui ) >> 15); 173647f87355Stsutsui count--; 173747f87355Stsutsui } 173847f87355Stsutsui 173947f87355Stsutsui return val; 174047f87355Stsutsui } 174147f87355Stsutsui 174247f87355Stsutsui static int 174347f87355Stsutsui udl_set_resolution(struct udl_softc *sc, const struct videomode *vmp) 174447f87355Stsutsui { 174547f87355Stsutsui uint16_t val; 174647f87355Stsutsui int start16, stride16, start8, stride8; 174747f87355Stsutsui 174847f87355Stsutsui /* set video memory offsets */ 174947f87355Stsutsui start16 = 0; 175047f87355Stsutsui stride16 = sc->sc_width * 2; 175147f87355Stsutsui start8 = stride16 * sc->sc_height; 175247f87355Stsutsui stride8 = sc->sc_width; 175347f87355Stsutsui udl_set_address(sc, start16, stride16, start8, stride8); 175447f87355Stsutsui 175547f87355Stsutsui /* write resolution values */ 175647f87355Stsutsui udl_reg_write_1(sc, UDL_REG_SYNC, 0x00); 175747f87355Stsutsui udl_reg_write_1(sc, UDL_REG_COLORDEPTH, UDL_REG_COLORDEPTH_16); 175847f87355Stsutsui val = vmp->htotal - vmp->hsync_start; 175947f87355Stsutsui udl_reg_write_2(sc, UDL_REG_XDISPLAYSTART, udl_lfsr(val)); 176047f87355Stsutsui val += vmp->hdisplay; 176147f87355Stsutsui udl_reg_write_2(sc, UDL_REG_XDISPLAYEND, udl_lfsr(val)); 176247f87355Stsutsui val = vmp->vtotal - vmp->vsync_start; 176347f87355Stsutsui udl_reg_write_2(sc, UDL_REG_YDISPLAYSTART, udl_lfsr(val)); 176447f87355Stsutsui val += vmp->vdisplay; 176547f87355Stsutsui udl_reg_write_2(sc, UDL_REG_YDISPLAYEND, udl_lfsr(val)); 176647f87355Stsutsui val = vmp->htotal - 1; 176747f87355Stsutsui udl_reg_write_2(sc, UDL_REG_XENDCOUNT, udl_lfsr(val)); 176847f87355Stsutsui val = vmp->hsync_end - vmp->hsync_start + 1; 176947f87355Stsutsui if (vmp->flags & VID_PHSYNC) { 177047f87355Stsutsui udl_reg_write_2(sc, UDL_REG_HSYNCSTART, udl_lfsr(1)); 177147f87355Stsutsui udl_reg_write_2(sc, UDL_REG_HSYNCEND, udl_lfsr(val)); 177247f87355Stsutsui } else { 177347f87355Stsutsui udl_reg_write_2(sc, UDL_REG_HSYNCSTART, udl_lfsr(val)); 177447f87355Stsutsui udl_reg_write_2(sc, UDL_REG_HSYNCEND, udl_lfsr(1)); 177547f87355Stsutsui } 177647f87355Stsutsui val = vmp->hdisplay; 177747f87355Stsutsui udl_reg_write_2(sc, UDL_REG_HPIXELS, val); 177847f87355Stsutsui val = vmp->vtotal; 177947f87355Stsutsui udl_reg_write_2(sc, UDL_REG_YENDCOUNT, udl_lfsr(val)); 178047f87355Stsutsui val = vmp->vsync_end - vmp->vsync_start; 178147f87355Stsutsui if (vmp->flags & VID_PVSYNC) { 178247f87355Stsutsui udl_reg_write_2(sc, UDL_REG_VSYNCSTART, udl_lfsr(0)); 178347f87355Stsutsui udl_reg_write_2(sc, UDL_REG_VSYNCEND, udl_lfsr(val)); 178447f87355Stsutsui } else { 178547f87355Stsutsui udl_reg_write_2(sc, UDL_REG_VSYNCSTART, udl_lfsr(val)); 178647f87355Stsutsui udl_reg_write_2(sc, UDL_REG_VSYNCEND, udl_lfsr(0)); 178747f87355Stsutsui } 178847f87355Stsutsui val = vmp->vdisplay; 178947f87355Stsutsui udl_reg_write_2(sc, UDL_REG_VPIXELS, val); 179047f87355Stsutsui val = vmp->dot_clock / 5; 179147f87355Stsutsui udl_reg_write_2(sc, UDL_REG_PIXELCLOCK5KHZ, bswap16(val)); 179247f87355Stsutsui udl_reg_write_1(sc, UDL_REG_SYNC, 0xff); 179347f87355Stsutsui 179447f87355Stsutsui if (udl_cmd_send(sc) != 0) 179547f87355Stsutsui return -1; 179647f87355Stsutsui 179747f87355Stsutsui /* clear screen */ 179847f87355Stsutsui udl_fill_rect(sc, 0, 0, 0, sc->sc_width, sc->sc_height); 179947f87355Stsutsui 180047f87355Stsutsui if (udl_cmd_send(sc) != 0) 180147f87355Stsutsui return -1; 180247f87355Stsutsui 180347f87355Stsutsui /* show framebuffer content */ 180447f87355Stsutsui udl_blank(sc, 0); 180547f87355Stsutsui 180647f87355Stsutsui if (udl_cmd_send(sc) != 0) 180747f87355Stsutsui return -1; 180847f87355Stsutsui 180947f87355Stsutsui sc->sc_blank = WSDISPLAYIO_VIDEO_ON; 181047f87355Stsutsui 181147f87355Stsutsui return 0; 181247f87355Stsutsui } 181347f87355Stsutsui 181447f87355Stsutsui static const struct videomode * 181547f87355Stsutsui udl_videomode_lookup(const char *name) 181647f87355Stsutsui { 181747f87355Stsutsui int i; 181847f87355Stsutsui 181947f87355Stsutsui for (i = 0; i < videomode_count; i++) 182047f87355Stsutsui if (strcmp(name, videomode_list[i].name) == 0) 182147f87355Stsutsui return &videomode_list[i]; 182247f87355Stsutsui 182347f87355Stsutsui return NULL; 182447f87355Stsutsui } 18257b977f0dSnat 18267b977f0dSnat static void 18277b977f0dSnat udl_update_thread(void *v) 18287b977f0dSnat { 18297b977f0dSnat struct udl_softc *sc = v; 18307b977f0dSnat uint16_t *fb, *fbcopy; 18313b0c992fSnat int offs, stride, count = 0; 18327b977f0dSnat 18337b977f0dSnat mutex_enter(&sc->sc_thread_mtx); 18347b977f0dSnat 18357b977f0dSnat for (;;) { 18367b977f0dSnat if (sc->sc_dying == true) { 18377b977f0dSnat mutex_exit(&sc->sc_thread_mtx); 18387b977f0dSnat kthread_exit(0); 18397b977f0dSnat } 18407b977f0dSnat 184163631654Snat if (sc->sc_thread_stop == true || sc->sc_fbmem == NULL || 184263631654Snat sc->sc_fbmem_prev == NULL || sc->sc_width <= 0) 18437b977f0dSnat goto thread_wait; 18447b977f0dSnat 18453b0c992fSnat if (sc->sc_clear == true) 18463b0c992fSnat count = 0; 18473b0c992fSnat sc->sc_clear = false; 18487b977f0dSnat 18493b0c992fSnat stride = uimin(sc->sc_width, UDL_CMD_WIDTH_MAX - 8); 18503b0c992fSnat stride /= 8; 18513b0c992fSnat fb = (uint16_t *)sc->sc_fbmem; 18523b0c992fSnat fbcopy = (uint16_t *)sc->sc_fbmem_prev; 18533b0c992fSnat for (offs = 0; offs < (sc->sc_height * sc->sc_width) - stride; 18543b0c992fSnat offs += stride) { 18553b0c992fSnat if (count % (hz / 5) == 0 || memcmp(&fb[offs], 18563b0c992fSnat &fbcopy[offs], stride * sizeof(uint16_t)) != 0) { 18573b0c992fSnat udl_draw_line(sc, &fb[offs], offs, stride); 18583b0c992fSnat memcpy(&fbcopy[offs], &fb[offs], stride * 18593b0c992fSnat sizeof(uint16_t)); 18603b0c992fSnat } 18613b0c992fSnat } 18623b0c992fSnat count++; 18633b0c992fSnat 1864bc47b06aSnat kpause("udlslp", false, 1, &sc->sc_thread_mtx); 18657b977f0dSnat continue; 18667b977f0dSnat 18677b977f0dSnat thread_wait: 18687b977f0dSnat cv_wait(&sc->sc_thread_cv, &sc->sc_thread_mtx); 18697b977f0dSnat } 18707b977f0dSnat } 1871c1b9bee0Snat 1872c1b9bee0Snat static inline void 1873c1b9bee0Snat udl_startstop(struct udl_softc *sc, bool stop) 1874c1b9bee0Snat { 1875c1b9bee0Snat mutex_enter(&sc->sc_thread_mtx); 1876c1b9bee0Snat sc->sc_thread_stop = stop; 18772a53858dSnat if (!stop) { 18782a53858dSnat sc->sc_clear = true; 1879c1b9bee0Snat cv_broadcast(&sc->sc_thread_cv); 18802a53858dSnat } 1881c1b9bee0Snat mutex_exit(&sc->sc_thread_mtx); 1882c1b9bee0Snat } 1883