1278799Shselasky/* $OpenBSD: udl.c,v 1.81 2014/12/09 07:05:06 doug Exp $ */ 2278799Shselasky/* $FreeBSD: stable/11/sys/dev/usb/video/udl.c 355578 2019-12-10 07:07:17Z hselasky $ */ 3278799Shselasky 4278799Shselasky/*- 5278799Shselasky * Copyright (c) 2015 Hans Petter Selasky <hselasky@freebsd.org> 6278799Shselasky * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org> 7278799Shselasky * 8278799Shselasky * Permission to use, copy, modify, and distribute this software for any 9278799Shselasky * purpose with or without fee is hereby granted, provided that the above 10278799Shselasky * copyright notice and this permission notice appear in all copies. 11278799Shselasky * 12278799Shselasky * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13278799Shselasky * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14278799Shselasky * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15278799Shselasky * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16278799Shselasky * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17278799Shselasky * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18278799Shselasky * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19278799Shselasky */ 20278799Shselasky 21278799Shselasky/* 22278799Shselasky * Driver for the "DisplayLink DL-120 / DL-160" graphic chips based on 23278799Shselasky * the reversed engineered specifications of Florian Echtler 24278799Shselasky * <floe@butterbrot.org>: 25278799Shselasky * 26278799Shselasky * http://floe.butterbrot.org/displaylink/doku.php 27278799Shselasky */ 28278799Shselasky 29278799Shselasky#include <sys/param.h> 30278799Shselasky#include <sys/bus.h> 31278799Shselasky#include <sys/callout.h> 32278799Shselasky#include <sys/conf.h> 33278799Shselasky#include <sys/kernel.h> 34278799Shselasky#include <sys/lock.h> 35278799Shselasky#include <sys/module.h> 36278799Shselasky#include <sys/mutex.h> 37278799Shselasky#include <sys/condvar.h> 38278799Shselasky#include <sys/sysctl.h> 39278799Shselasky#include <sys/systm.h> 40278799Shselasky#include <sys/consio.h> 41278799Shselasky#include <sys/fbio.h> 42278799Shselasky 43278799Shselasky#include <dev/fb/fbreg.h> 44278799Shselasky#include <dev/syscons/syscons.h> 45278799Shselasky 46278799Shselasky#include <dev/videomode/videomode.h> 47278799Shselasky#include <dev/videomode/edidvar.h> 48278799Shselasky 49278799Shselasky#include <dev/usb/usb.h> 50278799Shselasky#include <dev/usb/usbdi.h> 51278799Shselasky#include <dev/usb/usbdi_util.h> 52278799Shselasky#include "usbdevs.h" 53278799Shselasky 54278799Shselasky#include <dev/usb/video/udl.h> 55278799Shselasky 56278799Shselasky#include "fb_if.h" 57278799Shselasky 58278799Shselasky#undef DPRINTF 59278799Shselasky#undef DPRINTFN 60278799Shselasky#define USB_DEBUG_VAR udl_debug 61278799Shselasky#include <dev/usb/usb_debug.h> 62278799Shselasky 63279753Shselaskystatic SYSCTL_NODE(_hw_usb, OID_AUTO, udl, CTLFLAG_RW, 0, "USB UDL"); 64279753Shselasky 65278799Shselasky#ifdef USB_DEBUG 66278799Shselaskystatic int udl_debug = 0; 67278799Shselasky 68278799ShselaskySYSCTL_INT(_hw_usb_udl, OID_AUTO, debug, CTLFLAG_RWTUN, 69278799Shselasky &udl_debug, 0, "Debug level"); 70278799Shselasky#endif 71278799Shselasky 72279753Shselasky#define UDL_FPS_MAX 60 73279753Shselasky#define UDL_FPS_MIN 1 74279753Shselasky 75279753Shselaskystatic int udl_fps = 25; 76279753ShselaskySYSCTL_INT(_hw_usb_udl, OID_AUTO, fps, CTLFLAG_RWTUN, 77279753Shselasky &udl_fps, 0, "Frames Per Second, 1-60"); 78279753Shselasky 79281644Shselaskystatic struct mtx udl_buffer_mtx; 80281644Shselaskystatic struct udl_buffer_head udl_buffer_head; 81281644Shselasky 82281644ShselaskyMALLOC_DEFINE(M_USB_DL, "USB", "USB DisplayLink"); 83281644Shselasky 84278799Shselasky/* 85278799Shselasky * Prototypes. 86278799Shselasky */ 87278799Shselaskystatic usb_callback_t udl_bulk_write_callback; 88278799Shselasky 89278799Shselaskystatic device_probe_t udl_probe; 90278799Shselaskystatic device_attach_t udl_attach; 91278799Shselaskystatic device_detach_t udl_detach; 92278799Shselaskystatic fb_getinfo_t udl_fb_getinfo; 93278847Shselaskystatic fb_setblankmode_t udl_fb_setblankmode; 94278799Shselasky 95278799Shselaskystatic void udl_select_chip(struct udl_softc *, struct usb_attach_arg *); 96278799Shselaskystatic int udl_init_chip(struct udl_softc *); 97278799Shselaskystatic void udl_select_mode(struct udl_softc *); 98278799Shselaskystatic int udl_init_resolution(struct udl_softc *); 99278799Shselaskystatic void udl_fbmem_alloc(struct udl_softc *); 100278799Shselaskystatic int udl_cmd_write_buf_le16(struct udl_softc *, const uint8_t *, uint32_t, uint8_t, int); 101278799Shselaskystatic int udl_cmd_buf_copy_le16(struct udl_softc *, uint32_t, uint32_t, uint8_t, int); 102278799Shselaskystatic void udl_cmd_insert_int_1(struct udl_cmd_buf *, uint8_t); 103278799Shselaskystatic void udl_cmd_insert_int_3(struct udl_cmd_buf *, uint32_t); 104278799Shselaskystatic void udl_cmd_insert_buf_le16(struct udl_cmd_buf *, const uint8_t *, uint32_t); 105278799Shselaskystatic void udl_cmd_write_reg_1(struct udl_cmd_buf *, uint8_t, uint8_t); 106278799Shselaskystatic void udl_cmd_write_reg_3(struct udl_cmd_buf *, uint8_t, uint32_t); 107278799Shselaskystatic int udl_power_save(struct udl_softc *, int, int); 108278799Shselasky 109278799Shselaskystatic const struct usb_config udl_config[UDL_N_TRANSFER] = { 110278799Shselasky [UDL_BULK_WRITE_0] = { 111278799Shselasky .type = UE_BULK, 112278799Shselasky .endpoint = UE_ADDR_ANY, 113278799Shselasky .direction = UE_DIR_TX, 114278799Shselasky .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, 115278799Shselasky .bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES, 116278799Shselasky .callback = &udl_bulk_write_callback, 117278799Shselasky .frames = UDL_CMD_MAX_FRAMES, 118278799Shselasky .timeout = 5000, /* 5 seconds */ 119278799Shselasky }, 120278799Shselasky [UDL_BULK_WRITE_1] = { 121278799Shselasky .type = UE_BULK, 122278799Shselasky .endpoint = UE_ADDR_ANY, 123278799Shselasky .direction = UE_DIR_TX, 124278799Shselasky .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, 125278799Shselasky .bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES, 126278799Shselasky .callback = &udl_bulk_write_callback, 127278799Shselasky .frames = UDL_CMD_MAX_FRAMES, 128278799Shselasky .timeout = 5000, /* 5 seconds */ 129278799Shselasky }, 130278799Shselasky}; 131278799Shselasky 132278799Shselasky/* 133278799Shselasky * Driver glue. 134278799Shselasky */ 135278799Shselaskystatic devclass_t udl_devclass; 136278799Shselasky 137278799Shselaskystatic device_method_t udl_methods[] = { 138278799Shselasky DEVMETHOD(device_probe, udl_probe), 139278799Shselasky DEVMETHOD(device_attach, udl_attach), 140278799Shselasky DEVMETHOD(device_detach, udl_detach), 141278799Shselasky DEVMETHOD(fb_getinfo, udl_fb_getinfo), 142278799Shselasky DEVMETHOD_END 143278799Shselasky}; 144278799Shselasky 145278799Shselaskystatic driver_t udl_driver = { 146278799Shselasky .name = "udl", 147278799Shselasky .methods = udl_methods, 148278799Shselasky .size = sizeof(struct udl_softc), 149278799Shselasky}; 150278799Shselasky 151278799ShselaskyDRIVER_MODULE(udl, uhub, udl_driver, udl_devclass, NULL, NULL); 152278799ShselaskyMODULE_DEPEND(udl, usb, 1, 1, 1); 153278799ShselaskyMODULE_DEPEND(udl, fbd, 1, 1, 1); 154278799ShselaskyMODULE_DEPEND(udl, videomode, 1, 1, 1); 155278799ShselaskyMODULE_VERSION(udl, 1); 156278799Shselasky 157278799Shselasky/* 158278799Shselasky * Matching devices. 159278799Shselasky */ 160278799Shselaskystatic const STRUCT_USB_HOST_ID udl_devs[] = { 161278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U, DL120)}, 162278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U, DL120)}, 163278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020, DL160)}, 164278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220, DL165)}, 165278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60, DL160)}, 166278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI, DL160)}, 167278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10, DL120)}, 168278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI, DLUNK)}, 169278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008, DL160)}, 170278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK, DL160)}, 171278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571, DL160)}, 172278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061, DL195)}, 173278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK, DL165)}, 174278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI, DLUNK)}, 175278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0, DL120)}, 176278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV, DL160)}, 177278825Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE, DL160)}, 178278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70, DL125)}, 179278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2, DLUNK)}, 180297416Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)}, 181297416Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_ITEC, DL165)}, 182355578Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DVI_19, DL165)}, 183278799Shselasky}; 184278799Shselasky 185281644Shselaskystatic void 186281644Shselaskyudl_buffer_init(void *arg) 187281644Shselasky{ 188281644Shselasky mtx_init(&udl_buffer_mtx, "USB", "UDL", MTX_DEF); 189281644Shselasky TAILQ_INIT(&udl_buffer_head); 190281644Shselasky} 191281644ShselaskySYSINIT(udl_buffer_init, SI_SUB_LOCK, SI_ORDER_FIRST, udl_buffer_init, NULL); 192281644Shselasky 193281644ShselaskyCTASSERT(sizeof(struct udl_buffer) < PAGE_SIZE); 194281644Shselasky 195281644Shselaskystatic void * 196281644Shselaskyudl_buffer_alloc(uint32_t size) 197281644Shselasky{ 198281644Shselasky struct udl_buffer *buf; 199281644Shselasky mtx_lock(&udl_buffer_mtx); 200281644Shselasky TAILQ_FOREACH(buf, &udl_buffer_head, entry) { 201281644Shselasky if (buf->size == size) { 202281644Shselasky TAILQ_REMOVE(&udl_buffer_head, buf, entry); 203281644Shselasky break; 204281644Shselasky } 205281644Shselasky } 206281644Shselasky mtx_unlock(&udl_buffer_mtx); 207281644Shselasky if (buf != NULL) { 208282725Shselasky uint8_t *ptr = ((uint8_t *)buf) - size; 209281644Shselasky /* wipe and recycle buffer */ 210282725Shselasky memset(ptr, 0, size); 211282725Shselasky /* return buffer pointer */ 212282725Shselasky return (ptr); 213281644Shselasky } 214281644Shselasky /* allocate new buffer */ 215282725Shselasky return (malloc(size + sizeof(*buf), M_USB_DL, M_WAITOK | M_ZERO)); 216281644Shselasky} 217281644Shselasky 218281644Shselaskystatic void 219281644Shselaskyudl_buffer_free(void *_buf, uint32_t size) 220281644Shselasky{ 221281644Shselasky struct udl_buffer *buf; 222281644Shselasky 223282725Shselasky /* check for NULL pointer */ 224282725Shselasky if (_buf == NULL) 225281644Shselasky return; 226282725Shselasky /* compute pointer to recycle list */ 227282725Shselasky buf = (struct udl_buffer *)(((uint8_t *)_buf) + size); 228281644Shselasky 229281644Shselasky /* 230281644Shselasky * Memory mapped buffers should never be freed. 231281644Shselasky * Put display buffer into a recycle list. 232281644Shselasky */ 233281644Shselasky mtx_lock(&udl_buffer_mtx); 234281644Shselasky buf->size = size; 235281644Shselasky TAILQ_INSERT_TAIL(&udl_buffer_head, buf, entry); 236281644Shselasky mtx_unlock(&udl_buffer_mtx); 237281644Shselasky} 238281644Shselasky 239278799Shselaskystatic uint32_t 240278799Shselaskyudl_get_fb_size(struct udl_softc *sc) 241278799Shselasky{ 242278799Shselasky unsigned i = sc->sc_cur_mode; 243278799Shselasky 244278799Shselasky return ((uint32_t)udl_modes[i].hdisplay * 245278799Shselasky (uint32_t)udl_modes[i].vdisplay * 2); 246278799Shselasky} 247278799Shselasky 248278799Shselaskystatic uint32_t 249278799Shselaskyudl_get_fb_width(struct udl_softc *sc) 250278799Shselasky{ 251278799Shselasky unsigned i = sc->sc_cur_mode; 252278799Shselasky 253278851Shselasky return (udl_modes[i].hdisplay); 254278799Shselasky} 255278799Shselasky 256278799Shselaskystatic uint32_t 257278799Shselaskyudl_get_fb_height(struct udl_softc *sc) 258278799Shselasky{ 259278799Shselasky unsigned i = sc->sc_cur_mode; 260278799Shselasky 261278851Shselasky return (udl_modes[i].vdisplay); 262278799Shselasky} 263278799Shselasky 264278799Shselaskystatic uint32_t 265278799Shselaskyudl_get_fb_hz(struct udl_softc *sc) 266278799Shselasky{ 267278799Shselasky unsigned i = sc->sc_cur_mode; 268278799Shselasky 269278799Shselasky return (udl_modes[i].hz); 270278799Shselasky} 271278799Shselasky 272278799Shselaskystatic void 273278799Shselaskyudl_callout(void *arg) 274278799Shselasky{ 275278799Shselasky struct udl_softc *sc = arg; 276278799Shselasky const uint32_t max = udl_get_fb_size(sc); 277279753Shselasky int fps; 278278799Shselasky 279278799Shselasky if (sc->sc_power_save == 0) { 280279753Shselasky fps = udl_fps; 281279753Shselasky 282279753Shselasky /* figure out number of frames per second */ 283279753Shselasky if (fps < UDL_FPS_MIN) 284279753Shselasky fps = UDL_FPS_MIN; 285279753Shselasky else if (fps > UDL_FPS_MAX) 286279753Shselasky fps = UDL_FPS_MAX; 287279753Shselasky 288278799Shselasky if (sc->sc_sync_off >= max) 289278799Shselasky sc->sc_sync_off = 0; 290278799Shselasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 291278799Shselasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 292279753Shselasky } else { 293279753Shselasky fps = 1; 294278799Shselasky } 295279753Shselasky callout_reset(&sc->sc_callout, hz / fps, &udl_callout, sc); 296278799Shselasky} 297278799Shselasky 298278799Shselaskystatic int 299278799Shselaskyudl_probe(device_t dev) 300278799Shselasky{ 301278799Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 302278799Shselasky 303278799Shselasky if (uaa->usb_mode != USB_MODE_HOST) 304278799Shselasky return (ENXIO); 305278799Shselasky if (uaa->info.bConfigIndex != 0) 306278799Shselasky return (ENXIO); 307278799Shselasky if (uaa->info.bIfaceIndex != 0) 308278799Shselasky return (ENXIO); 309278799Shselasky 310278799Shselasky return (usbd_lookup_id_by_uaa(udl_devs, sizeof(udl_devs), uaa)); 311278799Shselasky} 312278799Shselasky 313278799Shselaskystatic int 314278799Shselaskyudl_attach(device_t dev) 315278799Shselasky{ 316278799Shselasky struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 317278799Shselasky struct sysctl_oid *tree = device_get_sysctl_tree(dev); 318278799Shselasky struct udl_softc *sc = device_get_softc(dev); 319278799Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 320278799Shselasky int error; 321278799Shselasky int i; 322278799Shselasky 323278799Shselasky device_set_usb_desc(dev); 324278799Shselasky 325278799Shselasky mtx_init(&sc->sc_mtx, "UDL lock", NULL, MTX_DEF); 326278799Shselasky cv_init(&sc->sc_cv, "UDLCV"); 327278799Shselasky callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 328278799Shselasky sc->sc_udev = uaa->device; 329278799Shselasky 330278799Shselasky error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 331278799Shselasky sc->sc_xfer, udl_config, UDL_N_TRANSFER, sc, &sc->sc_mtx); 332278799Shselasky 333278799Shselasky if (error) { 334278799Shselasky DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error)); 335278799Shselasky goto detach; 336278799Shselasky } 337278799Shselasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_0], &sc->sc_xfer_head[0]); 338278799Shselasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_1], &sc->sc_xfer_head[1]); 339278799Shselasky 340278799Shselasky TAILQ_INIT(&sc->sc_xfer_head[0]); 341278799Shselasky TAILQ_INIT(&sc->sc_xfer_head[1]); 342278799Shselasky TAILQ_INIT(&sc->sc_cmd_buf_free); 343278799Shselasky TAILQ_INIT(&sc->sc_cmd_buf_pending); 344278799Shselasky 345278799Shselasky sc->sc_def_chip = -1; 346278799Shselasky sc->sc_chip = USB_GET_DRIVER_INFO(uaa); 347278799Shselasky sc->sc_def_mode = -1; 348278799Shselasky sc->sc_cur_mode = UDL_MAX_MODES; 349278799Shselasky 350278799Shselasky /* Allow chip ID to be overwritten */ 351278799Shselasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid_force", 352278799Shselasky CTLFLAG_RWTUN, &sc->sc_def_chip, 0, "chip ID"); 353278799Shselasky 354278799Shselasky /* Export current chip ID */ 355278799Shselasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid", 356278799Shselasky CTLFLAG_RD, &sc->sc_chip, 0, "chip ID"); 357278799Shselasky 358278799Shselasky if (sc->sc_def_chip > -1 && sc->sc_def_chip <= DLMAX) { 359278799Shselasky device_printf(dev, "Forcing chip ID to 0x%04x\n", sc->sc_def_chip); 360278799Shselasky sc->sc_chip = sc->sc_def_chip; 361278799Shselasky } 362278799Shselasky /* 363278799Shselasky * The product might have more than one chip 364278799Shselasky */ 365278799Shselasky if (sc->sc_chip == DLUNK) 366278799Shselasky udl_select_chip(sc, uaa); 367278799Shselasky 368278799Shselasky for (i = 0; i != UDL_CMD_MAX_BUFFERS; i++) { 369278799Shselasky struct udl_cmd_buf *cb = &sc->sc_cmd_buf_temp[i]; 370278799Shselasky 371278799Shselasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 372278799Shselasky } 373278799Shselasky 374278799Shselasky /* 375278799Shselasky * Initialize chip. 376278799Shselasky */ 377278799Shselasky error = udl_init_chip(sc); 378278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 379278799Shselasky goto detach; 380278799Shselasky 381278799Shselasky /* 382278799Shselasky * Select edid mode. 383278799Shselasky */ 384278799Shselasky udl_select_mode(sc); 385278799Shselasky 386278799Shselasky /* Allow default mode to be overwritten */ 387278799Shselasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode_force", 388278799Shselasky CTLFLAG_RWTUN, &sc->sc_def_mode, 0, "mode"); 389278799Shselasky 390278799Shselasky /* Export current mode */ 391278799Shselasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode", 392278799Shselasky CTLFLAG_RD, &sc->sc_cur_mode, 0, "mode"); 393278799Shselasky 394278799Shselasky i = sc->sc_def_mode; 395278799Shselasky if (i > -1 && i < UDL_MAX_MODES) { 396278799Shselasky if (udl_modes[i].chip <= sc->sc_chip) { 397278799Shselasky device_printf(dev, "Forcing mode to %d\n", i); 398278799Shselasky sc->sc_cur_mode = i; 399278799Shselasky } 400278799Shselasky } 401278799Shselasky /* Printout current mode */ 402278799Shselasky device_printf(dev, "Mode selected %dx%d @ %dHz\n", 403278799Shselasky (int)udl_get_fb_width(sc), 404278799Shselasky (int)udl_get_fb_height(sc), 405278799Shselasky (int)udl_get_fb_hz(sc)); 406278799Shselasky 407278799Shselasky udl_init_resolution(sc); 408278799Shselasky 409278799Shselasky /* Allocate frame buffer */ 410278799Shselasky udl_fbmem_alloc(sc); 411278799Shselasky 412278799Shselasky UDL_LOCK(sc); 413278799Shselasky udl_callout(sc); 414278799Shselasky UDL_UNLOCK(sc); 415278799Shselasky 416278799Shselasky sc->sc_fb_info.fb_name = device_get_nameunit(dev); 417278799Shselasky sc->sc_fb_info.fb_size = sc->sc_fb_size; 418278799Shselasky sc->sc_fb_info.fb_bpp = 16; 419278799Shselasky sc->sc_fb_info.fb_depth = 16; 420278799Shselasky sc->sc_fb_info.fb_width = udl_get_fb_width(sc); 421278799Shselasky sc->sc_fb_info.fb_height = udl_get_fb_height(sc); 422278799Shselasky sc->sc_fb_info.fb_stride = sc->sc_fb_info.fb_width * 2; 423278824Shselasky sc->sc_fb_info.fb_pbase = 0; 424278799Shselasky sc->sc_fb_info.fb_vbase = (uintptr_t)sc->sc_fb_addr; 425278847Shselasky sc->sc_fb_info.fb_priv = sc; 426278847Shselasky sc->sc_fb_info.setblankmode = &udl_fb_setblankmode; 427278799Shselasky 428278799Shselasky sc->sc_fbdev = device_add_child(dev, "fbd", -1); 429278799Shselasky if (sc->sc_fbdev == NULL) 430278799Shselasky goto detach; 431278799Shselasky if (device_probe_and_attach(sc->sc_fbdev) != 0) 432278799Shselasky goto detach; 433278799Shselasky 434278799Shselasky return (0); 435278799Shselasky 436278799Shselaskydetach: 437278799Shselasky udl_detach(dev); 438278799Shselasky 439278799Shselasky return (ENXIO); 440278799Shselasky} 441278799Shselasky 442278799Shselaskystatic int 443278799Shselaskyudl_detach(device_t dev) 444278799Shselasky{ 445278799Shselasky struct udl_softc *sc = device_get_softc(dev); 446278799Shselasky 447308401Shselasky /* delete all child devices */ 448308401Shselasky device_delete_children(dev); 449278799Shselasky 450278799Shselasky UDL_LOCK(sc); 451278799Shselasky sc->sc_gone = 1; 452278799Shselasky callout_stop(&sc->sc_callout); 453278799Shselasky UDL_UNLOCK(sc); 454278799Shselasky 455278799Shselasky usbd_transfer_unsetup(sc->sc_xfer, UDL_N_TRANSFER); 456278799Shselasky 457278799Shselasky callout_drain(&sc->sc_callout); 458278799Shselasky 459278799Shselasky mtx_destroy(&sc->sc_mtx); 460278799Shselasky cv_destroy(&sc->sc_cv); 461278799Shselasky 462281644Shselasky /* put main framebuffer into a recycle list, if any */ 463281644Shselasky udl_buffer_free(sc->sc_fb_addr, sc->sc_fb_size); 464278799Shselasky 465281644Shselasky /* free shadow framebuffer memory, if any */ 466281644Shselasky free(sc->sc_fb_copy, M_USB_DL); 467281644Shselasky 468278799Shselasky return (0); 469278799Shselasky} 470278799Shselasky 471278799Shselaskystatic struct fb_info * 472278799Shselaskyudl_fb_getinfo(device_t dev) 473278799Shselasky{ 474278799Shselasky struct udl_softc *sc = device_get_softc(dev); 475278799Shselasky 476278799Shselasky return (&sc->sc_fb_info); 477278799Shselasky} 478278799Shselasky 479278799Shselaskystatic int 480278847Shselaskyudl_fb_setblankmode(void *arg, int mode) 481278799Shselasky{ 482278847Shselasky struct udl_softc *sc = arg; 483278799Shselasky 484278799Shselasky switch (mode) { 485278799Shselasky case V_DISPLAY_ON: 486278799Shselasky udl_power_save(sc, 1, M_WAITOK); 487278799Shselasky break; 488278799Shselasky case V_DISPLAY_BLANK: 489278799Shselasky udl_power_save(sc, 1, M_WAITOK); 490278799Shselasky if (sc->sc_fb_addr != 0) { 491278799Shselasky const uint32_t max = udl_get_fb_size(sc); 492278799Shselasky 493278799Shselasky memset((void *)sc->sc_fb_addr, 0, max); 494278799Shselasky } 495278799Shselasky break; 496278799Shselasky case V_DISPLAY_STAND_BY: 497278799Shselasky case V_DISPLAY_SUSPEND: 498278799Shselasky udl_power_save(sc, 0, M_WAITOK); 499278799Shselasky break; 500278799Shselasky } 501278799Shselasky return (0); 502278799Shselasky} 503278799Shselasky 504278799Shselaskystatic struct udl_cmd_buf * 505278852Shselaskyudl_cmd_buf_alloc_locked(struct udl_softc *sc, int flags) 506278799Shselasky{ 507278799Shselasky struct udl_cmd_buf *cb; 508278799Shselasky 509278799Shselasky while ((cb = TAILQ_FIRST(&sc->sc_cmd_buf_free)) == NULL) { 510278799Shselasky if (flags != M_WAITOK) 511278799Shselasky break; 512278799Shselasky cv_wait(&sc->sc_cv, &sc->sc_mtx); 513278799Shselasky } 514278799Shselasky if (cb != NULL) { 515278799Shselasky TAILQ_REMOVE(&sc->sc_cmd_buf_free, cb, entry); 516278799Shselasky cb->off = 0; 517278799Shselasky } 518278852Shselasky return (cb); 519278852Shselasky} 520278852Shselasky 521278852Shselaskystatic struct udl_cmd_buf * 522278852Shselaskyudl_cmd_buf_alloc(struct udl_softc *sc, int flags) 523278852Shselasky{ 524278852Shselasky struct udl_cmd_buf *cb; 525278852Shselasky 526278852Shselasky UDL_LOCK(sc); 527278852Shselasky cb = udl_cmd_buf_alloc_locked(sc, flags); 528278799Shselasky UDL_UNLOCK(sc); 529278799Shselasky return (cb); 530278799Shselasky} 531278799Shselasky 532278799Shselaskystatic void 533278799Shselaskyudl_cmd_buf_send(struct udl_softc *sc, struct udl_cmd_buf *cb) 534278799Shselasky{ 535278799Shselasky UDL_LOCK(sc); 536278799Shselasky if (sc->sc_gone) { 537278799Shselasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 538278799Shselasky } else { 539278799Shselasky /* mark end of command stack */ 540278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 541278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_EOC); 542278799Shselasky 543278799Shselasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_pending, cb, entry); 544278799Shselasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 545278799Shselasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 546278799Shselasky } 547278799Shselasky UDL_UNLOCK(sc); 548278799Shselasky} 549278799Shselasky 550278799Shselaskystatic struct udl_cmd_buf * 551278852Shselaskyudl_fb_synchronize_locked(struct udl_softc *sc) 552278799Shselasky{ 553278799Shselasky const uint32_t max = udl_get_fb_size(sc); 554278799Shselasky 555278824Shselasky /* check if framebuffer is not ready */ 556278824Shselasky if (sc->sc_fb_addr == NULL || 557278824Shselasky sc->sc_fb_copy == NULL) 558278824Shselasky return (NULL); 559278824Shselasky 560278799Shselasky while (sc->sc_sync_off < max) { 561278799Shselasky uint32_t delta = max - sc->sc_sync_off; 562278799Shselasky 563278799Shselasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 564278799Shselasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 565278799Shselasky if (bcmp(sc->sc_fb_addr + sc->sc_sync_off, sc->sc_fb_copy + sc->sc_sync_off, delta) != 0) { 566278799Shselasky struct udl_cmd_buf *cb; 567278799Shselasky 568278852Shselasky cb = udl_cmd_buf_alloc_locked(sc, M_NOWAIT); 569278799Shselasky if (cb == NULL) 570278799Shselasky goto done; 571278799Shselasky memcpy(sc->sc_fb_copy + sc->sc_sync_off, 572278799Shselasky sc->sc_fb_addr + sc->sc_sync_off, delta); 573278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 574278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 575278799Shselasky udl_cmd_insert_int_3(cb, sc->sc_sync_off); 576278799Shselasky udl_cmd_insert_int_1(cb, delta / 2); 577278799Shselasky udl_cmd_insert_buf_le16(cb, sc->sc_fb_copy + sc->sc_sync_off, delta); 578278799Shselasky sc->sc_sync_off += delta; 579278799Shselasky return (cb); 580278799Shselasky } else { 581278799Shselasky sc->sc_sync_off += delta; 582278799Shselasky } 583278799Shselasky } 584278799Shselaskydone: 585278799Shselasky return (NULL); 586278799Shselasky} 587278799Shselasky 588278799Shselaskystatic void 589278799Shselaskyudl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 590278799Shselasky{ 591278799Shselasky struct udl_softc *sc = usbd_xfer_softc(xfer); 592278799Shselasky struct udl_cmd_head *phead = usbd_xfer_get_priv(xfer); 593278799Shselasky struct udl_cmd_buf *cb; 594278799Shselasky unsigned i; 595278799Shselasky 596278799Shselasky switch (USB_GET_STATE(xfer)) { 597278799Shselasky case USB_ST_TRANSFERRED: 598278799Shselasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 599278799Shselasky case USB_ST_SETUP: 600278799Shselaskytr_setup: 601278799Shselasky for (i = 0; i != UDL_CMD_MAX_FRAMES; i++) { 602278799Shselasky cb = TAILQ_FIRST(&sc->sc_cmd_buf_pending); 603278799Shselasky if (cb == NULL) { 604278852Shselasky cb = udl_fb_synchronize_locked(sc); 605278799Shselasky if (cb == NULL) 606278799Shselasky break; 607278824Shselasky } else { 608278824Shselasky TAILQ_REMOVE(&sc->sc_cmd_buf_pending, cb, entry); 609278799Shselasky } 610278799Shselasky TAILQ_INSERT_TAIL(phead, cb, entry); 611278799Shselasky usbd_xfer_set_frame_data(xfer, i, cb->buf, cb->off); 612278799Shselasky } 613278799Shselasky if (i != 0) { 614278799Shselasky usbd_xfer_set_frames(xfer, i); 615278799Shselasky usbd_transfer_submit(xfer); 616278799Shselasky } 617278799Shselasky break; 618278799Shselasky default: 619278799Shselasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 620278799Shselasky if (error != USB_ERR_CANCELLED) { 621278799Shselasky /* try clear stall first */ 622278799Shselasky usbd_xfer_set_stall(xfer); 623278799Shselasky goto tr_setup; 624278799Shselasky } 625278799Shselasky break; 626278799Shselasky } 627278799Shselasky /* wakeup any waiters */ 628278799Shselasky cv_signal(&sc->sc_cv); 629278799Shselasky} 630278799Shselasky 631278799Shselaskystatic int 632278799Shselaskyudl_power_save(struct udl_softc *sc, int on, int flags) 633278799Shselasky{ 634278799Shselasky struct udl_cmd_buf *cb; 635278799Shselasky 636278799Shselasky /* get new buffer */ 637278799Shselasky cb = udl_cmd_buf_alloc(sc, flags); 638278799Shselasky if (cb == NULL) 639278799Shselasky return (EAGAIN); 640278799Shselasky 641278799Shselasky DPRINTF("screen %s\n", on ? "ON" : "OFF"); 642278799Shselasky 643278799Shselasky sc->sc_power_save = on ? 0 : 1; 644278799Shselasky 645278799Shselasky if (on) 646278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 647278799Shselasky else 648278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF); 649278799Shselasky 650278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 651278799Shselasky udl_cmd_buf_send(sc, cb); 652278799Shselasky return (0); 653278799Shselasky} 654278799Shselasky 655278799Shselaskystatic int 656278799Shselaskyudl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r, 657278799Shselasky uint16_t index, uint16_t value, uint8_t *buf, size_t len) 658278799Shselasky{ 659278799Shselasky usb_device_request_t req; 660278799Shselasky int error; 661278799Shselasky 662278799Shselasky req.bmRequestType = rt; 663278799Shselasky req.bRequest = r; 664278799Shselasky USETW(req.wIndex, index); 665278799Shselasky USETW(req.wValue, value); 666278799Shselasky USETW(req.wLength, len); 667278799Shselasky 668278799Shselasky error = usbd_do_request_flags(sc->sc_udev, NULL, 669278799Shselasky &req, buf, 0, NULL, USB_DEFAULT_TIMEOUT); 670278799Shselasky 671278799Shselasky DPRINTF("%s\n", usbd_errstr(error)); 672278799Shselasky 673278799Shselasky return (error); 674278799Shselasky} 675278799Shselasky 676278799Shselaskystatic int 677278799Shselaskyudl_poll(struct udl_softc *sc, uint32_t *buf) 678278799Shselasky{ 679278799Shselasky uint32_t lbuf; 680278799Shselasky int error; 681278799Shselasky 682278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 683278799Shselasky UDL_CTRL_CMD_POLL, 0x0000, 0x0000, (uint8_t *)&lbuf, sizeof(lbuf)); 684278799Shselasky if (error == USB_ERR_NORMAL_COMPLETION) 685278799Shselasky *buf = le32toh(lbuf); 686278799Shselasky return (error); 687278799Shselasky} 688278799Shselasky 689278799Shselaskystatic int 690278799Shselaskyudl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf) 691278799Shselasky{ 692278799Shselasky uint8_t lbuf[1]; 693278799Shselasky int error; 694278799Shselasky 695278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 696278799Shselasky UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1); 697278799Shselasky if (error == USB_ERR_NORMAL_COMPLETION) 698278799Shselasky *buf = *(uint8_t *)lbuf; 699278799Shselasky return (error); 700278799Shselasky} 701278799Shselasky 702278799Shselaskystatic int 703278799Shselaskyudl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf) 704278799Shselasky{ 705278799Shselasky int error; 706278799Shselasky 707278799Shselasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 708278799Shselasky UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1); 709278799Shselasky return (error); 710278799Shselasky} 711278799Shselasky 712278799Shselaskystatic int 713278799Shselaskyudl_read_edid(struct udl_softc *sc, uint8_t *buf) 714278799Shselasky{ 715278799Shselasky uint8_t lbuf[64]; 716278799Shselasky uint16_t offset; 717278799Shselasky int error; 718278799Shselasky 719278799Shselasky offset = 0; 720278799Shselasky 721278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 722278799Shselasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 723278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 724278799Shselasky goto fail; 725278799Shselasky bcopy(lbuf + 1, buf + offset, 63); 726278799Shselasky offset += 63; 727278799Shselasky 728278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 729278799Shselasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 730278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 731278799Shselasky goto fail; 732278799Shselasky bcopy(lbuf + 1, buf + offset, 63); 733278799Shselasky offset += 63; 734278799Shselasky 735278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 736278799Shselasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3); 737278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 738278799Shselasky goto fail; 739278799Shselasky bcopy(lbuf + 1, buf + offset, 2); 740278799Shselaskyfail: 741278799Shselasky return (error); 742278799Shselasky} 743278799Shselasky 744278799Shselaskystatic uint8_t 745278799Shselaskyudl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t hz, 746278799Shselasky uint16_t chip, uint32_t clock) 747278799Shselasky{ 748278799Shselasky uint8_t idx; 749278799Shselasky 750278799Shselasky /* 751278799Shselasky * Check first if we have a matching mode with pixelclock 752278799Shselasky */ 753278799Shselasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 754278799Shselasky if ((udl_modes[idx].hdisplay == hdisplay) && 755278799Shselasky (udl_modes[idx].vdisplay == vdisplay) && 756278799Shselasky (udl_modes[idx].clock == clock) && 757278799Shselasky (udl_modes[idx].chip <= chip)) { 758278799Shselasky return (idx); 759278799Shselasky } 760278799Shselasky } 761278799Shselasky 762278799Shselasky /* 763278799Shselasky * If not, check for matching mode with update frequency 764278799Shselasky */ 765278799Shselasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 766278799Shselasky if ((udl_modes[idx].hdisplay == hdisplay) && 767278799Shselasky (udl_modes[idx].vdisplay == vdisplay) && 768278799Shselasky (udl_modes[idx].hz == hz) && 769278799Shselasky (udl_modes[idx].chip <= chip)) { 770278799Shselasky return (idx); 771278799Shselasky } 772278799Shselasky } 773278799Shselasky return (idx); 774278799Shselasky} 775278799Shselasky 776278799Shselaskystatic void 777278799Shselaskyudl_select_chip(struct udl_softc *sc, struct usb_attach_arg *uaa) 778278799Shselasky{ 779278799Shselasky const char *pserial; 780278799Shselasky 781278799Shselasky pserial = usb_get_serial(uaa->device); 782278799Shselasky 783278799Shselasky sc->sc_chip = DL120; 784278799Shselasky 785278799Shselasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 786278799Shselasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_WSDVI)) { 787278799Shselasky 788278799Shselasky /* 789278799Shselasky * WS Tech DVI is DL120 or DL160. All deviced uses the 790278799Shselasky * same revision (0.04) so iSerialNumber must be used 791278799Shselasky * to determin which chip it is. 792278799Shselasky */ 793278799Shselasky 794278799Shselasky if (strlen(pserial) > 7) { 795278799Shselasky if (strncmp(pserial, "0198-13", 7) == 0) 796278799Shselasky sc->sc_chip = DL160; 797278799Shselasky } 798278799Shselasky DPRINTF("iSerialNumber (%s) used to select chip (%d)\n", 799278799Shselasky pserial, sc->sc_chip); 800278799Shselasky } 801278799Shselasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 802278799Shselasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_SWDVI)) { 803278799Shselasky 804278799Shselasky /* 805278799Shselasky * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision 806278799Shselasky * can be used to differ between DL1x0 and DL1x5. Minor to 807278799Shselasky * differ between DL1x5. iSerialNumber seems not to be uniqe. 808278799Shselasky */ 809278799Shselasky 810278799Shselasky sc->sc_chip = DL160; 811278799Shselasky 812278799Shselasky if (uaa->info.bcdDevice >= 0x100) { 813278799Shselasky sc->sc_chip = DL165; 814278799Shselasky if (uaa->info.bcdDevice == 0x104) 815278799Shselasky sc->sc_chip = DL195; 816278799Shselasky if (uaa->info.bcdDevice == 0x108) 817278799Shselasky sc->sc_chip = DL125; 818278799Shselasky } 819278799Shselasky DPRINTF("bcdDevice (%02x) used to select chip (%d)\n", 820278799Shselasky uaa->info.bcdDevice, sc->sc_chip); 821278799Shselasky } 822278799Shselasky} 823278799Shselasky 824278799Shselaskystatic int 825278799Shselaskyudl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len) 826278799Shselasky{ 827278799Shselasky int error; 828278799Shselasky 829278799Shselasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 830278799Shselasky UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len); 831278799Shselasky return (error); 832278799Shselasky} 833278799Shselasky 834278799Shselaskystatic void 835278799Shselaskyudl_fbmem_alloc(struct udl_softc *sc) 836278799Shselasky{ 837278799Shselasky uint32_t size; 838278799Shselasky 839278799Shselasky size = udl_get_fb_size(sc); 840278799Shselasky size = round_page(size); 841281644Shselasky /* check for zero size */ 842281644Shselasky if (size == 0) 843281644Shselasky size = PAGE_SIZE; 844279753Shselasky /* 845279753Shselasky * It is assumed that allocations above PAGE_SIZE bytes will 846279753Shselasky * be PAGE_SIZE aligned for use with mmap() 847279753Shselasky */ 848281644Shselasky sc->sc_fb_addr = udl_buffer_alloc(size); 849281644Shselasky sc->sc_fb_copy = malloc(size, M_USB_DL, M_WAITOK | M_ZERO); 850278799Shselasky sc->sc_fb_size = size; 851278799Shselasky} 852278799Shselasky 853278799Shselaskystatic void 854278799Shselaskyudl_cmd_insert_int_1(struct udl_cmd_buf *cb, uint8_t value) 855278799Shselasky{ 856278799Shselasky 857278799Shselasky cb->buf[cb->off] = value; 858278799Shselasky cb->off += 1; 859278799Shselasky} 860278799Shselasky 861278799Shselasky#if 0 862278799Shselaskystatic void 863278799Shselaskyudl_cmd_insert_int_2(struct udl_cmd_buf *cb, uint16_t value) 864278799Shselasky{ 865278799Shselasky uint16_t lvalue; 866278799Shselasky 867278799Shselasky lvalue = htobe16(value); 868278799Shselasky bcopy(&lvalue, cb->buf + cb->off, 2); 869278799Shselasky 870278799Shselasky cb->off += 2; 871278799Shselasky} 872278799Shselasky 873278799Shselasky#endif 874278799Shselasky 875278799Shselaskystatic void 876278799Shselaskyudl_cmd_insert_int_3(struct udl_cmd_buf *cb, uint32_t value) 877278799Shselasky{ 878278799Shselasky uint32_t lvalue; 879278799Shselasky 880278799Shselasky#if BYTE_ORDER == BIG_ENDIAN 881278799Shselasky lvalue = htobe32(value) << 8; 882278799Shselasky#else 883278799Shselasky lvalue = htobe32(value) >> 8; 884278799Shselasky#endif 885278799Shselasky bcopy(&lvalue, cb->buf + cb->off, 3); 886278799Shselasky 887278799Shselasky cb->off += 3; 888278799Shselasky} 889278799Shselasky 890278799Shselasky#if 0 891278799Shselaskystatic void 892278799Shselaskyudl_cmd_insert_int_4(struct udl_cmd_buf *cb, uint32_t value) 893278799Shselasky{ 894278799Shselasky uint32_t lvalue; 895278799Shselasky 896278799Shselasky lvalue = htobe32(value); 897278799Shselasky bcopy(&lvalue, cb->buf + cb->off, 4); 898278799Shselasky 899278799Shselasky cb->off += 4; 900278799Shselasky} 901278799Shselasky 902278799Shselasky#endif 903278799Shselasky 904278799Shselaskystatic void 905278799Shselaskyudl_cmd_insert_buf_le16(struct udl_cmd_buf *cb, const uint8_t *buf, uint32_t len) 906278799Shselasky{ 907278799Shselasky uint32_t x; 908278799Shselasky 909278799Shselasky for (x = 0; x != len; x += 2) { 910278799Shselasky /* byte swap from little endian to big endian */ 911278799Shselasky cb->buf[cb->off + x + 0] = buf[x + 1]; 912278799Shselasky cb->buf[cb->off + x + 1] = buf[x + 0]; 913278799Shselasky } 914278799Shselasky cb->off += len; 915278799Shselasky} 916278799Shselasky 917278799Shselaskystatic void 918278799Shselaskyudl_cmd_write_reg_1(struct udl_cmd_buf *cb, uint8_t reg, uint8_t val) 919278799Shselasky{ 920278799Shselasky 921278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 922278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_REG_WRITE_1); 923278799Shselasky udl_cmd_insert_int_1(cb, reg); 924278799Shselasky udl_cmd_insert_int_1(cb, val); 925278799Shselasky} 926278799Shselasky 927278799Shselaskystatic void 928278799Shselaskyudl_cmd_write_reg_3(struct udl_cmd_buf *cb, uint8_t reg, uint32_t val) 929278799Shselasky{ 930278799Shselasky 931278799Shselasky udl_cmd_write_reg_1(cb, reg + 0, (val >> 16) & 0xff); 932278799Shselasky udl_cmd_write_reg_1(cb, reg + 1, (val >> 8) & 0xff); 933278799Shselasky udl_cmd_write_reg_1(cb, reg + 2, (val >> 0) & 0xff); 934278799Shselasky} 935278799Shselasky 936278799Shselaskystatic int 937278799Shselaskyudl_init_chip(struct udl_softc *sc) 938278799Shselasky{ 939278799Shselasky uint32_t ui32; 940278799Shselasky uint8_t ui8; 941278799Shselasky int error; 942278799Shselasky 943278799Shselasky error = udl_poll(sc, &ui32); 944278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 945278799Shselasky return (error); 946278799Shselasky DPRINTF("poll=0x%08x\n", ui32); 947278799Shselasky 948278799Shselasky /* Some products may use later chip too */ 949278799Shselasky switch (ui32 & 0xff) { 950278799Shselasky case 0xf1: /* DL1x5 */ 951278799Shselasky switch (sc->sc_chip) { 952278799Shselasky case DL120: 953278799Shselasky sc->sc_chip = DL125; 954278799Shselasky break; 955278799Shselasky case DL160: 956278799Shselasky sc->sc_chip = DL165; 957278799Shselasky break; 958278799Shselasky } 959278799Shselasky break; 960278799Shselasky } 961278799Shselasky DPRINTF("chip 0x%04x\n", sc->sc_chip); 962278799Shselasky 963278799Shselasky error = udl_read_1(sc, 0xc484, &ui8); 964278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 965278799Shselasky return (error); 966278799Shselasky DPRINTF("read 0x%02x from 0xc484\n", ui8); 967278799Shselasky 968278799Shselasky error = udl_write_1(sc, 0xc41f, 0x01); 969278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 970278799Shselasky return (error); 971278799Shselasky DPRINTF("write 0x01 to 0xc41f\n"); 972278799Shselasky 973278799Shselasky error = udl_read_edid(sc, sc->sc_edid); 974278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 975278799Shselasky return (error); 976278799Shselasky DPRINTF("read EDID\n"); 977278799Shselasky 978278799Shselasky error = udl_set_enc_key(sc, __DECONST(void *, udl_null_key_1), 979278799Shselasky sizeof(udl_null_key_1)); 980278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 981278799Shselasky return (error); 982278799Shselasky DPRINTF("set encryption key\n"); 983278799Shselasky 984278799Shselasky error = udl_write_1(sc, 0xc40b, 0x00); 985278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 986278799Shselasky return (error); 987278799Shselasky DPRINTF("write 0x00 to 0xc40b\n"); 988278799Shselasky 989278799Shselasky return (USB_ERR_NORMAL_COMPLETION); 990278799Shselasky} 991278799Shselasky 992278799Shselaskystatic void 993278799Shselaskyudl_init_fb_offsets(struct udl_cmd_buf *cb, uint32_t start16, uint32_t stride16, 994278799Shselasky uint32_t start8, uint32_t stride8) 995278799Shselasky{ 996278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 997278799Shselasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START16, start16); 998278799Shselasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE16, stride16); 999278799Shselasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START8, start8); 1000278799Shselasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE8, stride8); 1001278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 1002278799Shselasky} 1003278799Shselasky 1004278799Shselaskystatic int 1005278799Shselaskyudl_init_resolution(struct udl_softc *sc) 1006278799Shselasky{ 1007278799Shselasky const uint32_t max = udl_get_fb_size(sc); 1008278799Shselasky const uint8_t *buf = udl_modes[sc->sc_cur_mode].mode; 1009278799Shselasky struct udl_cmd_buf *cb; 1010278799Shselasky uint32_t delta; 1011278799Shselasky uint32_t i; 1012278799Shselasky int error; 1013278799Shselasky 1014278799Shselasky /* get new buffer */ 1015278799Shselasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 1016278799Shselasky if (cb == NULL) 1017278799Shselasky return (EAGAIN); 1018278799Shselasky 1019278799Shselasky /* write resolution values and set video memory offsets */ 1020278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 1021278799Shselasky for (i = 0; i < UDL_MODE_SIZE; i++) 1022278799Shselasky udl_cmd_write_reg_1(cb, i, buf[i]); 1023278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 1024278799Shselasky 1025278799Shselasky udl_init_fb_offsets(cb, 0x000000, 0x000a00, 0x555555, 0x000500); 1026278799Shselasky udl_cmd_buf_send(sc, cb); 1027278799Shselasky 1028278799Shselasky /* fill screen with black color */ 1029278799Shselasky for (i = 0; i < max; i += delta) { 1030278799Shselasky static const uint8_t udl_black[UDL_CMD_MAX_PIXEL_COUNT * 2] __aligned(4); 1031278799Shselasky 1032278799Shselasky delta = max - i; 1033278799Shselasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 1034278799Shselasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 1035278799Shselasky if (i == 0) 1036278799Shselasky error = udl_cmd_write_buf_le16(sc, udl_black, i, delta / 2, M_WAITOK); 1037278799Shselasky else 1038278799Shselasky error = udl_cmd_buf_copy_le16(sc, 0, i, delta / 2, M_WAITOK); 1039278799Shselasky if (error) 1040278799Shselasky return (error); 1041278799Shselasky } 1042278799Shselasky 1043278799Shselasky /* get new buffer */ 1044278799Shselasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 1045278799Shselasky if (cb == NULL) 1046278799Shselasky return (EAGAIN); 1047278799Shselasky 1048278799Shselasky /* show framebuffer content */ 1049278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 1050278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 1051278799Shselasky udl_cmd_buf_send(sc, cb); 1052278799Shselasky return (0); 1053278799Shselasky} 1054278799Shselasky 1055278799Shselaskystatic void 1056278799Shselaskyudl_select_mode(struct udl_softc *sc) 1057278799Shselasky{ 1058278799Shselasky struct udl_mode mode; 1059278799Shselasky int index = UDL_MAX_MODES; 1060278799Shselasky int i; 1061278799Shselasky 1062278799Shselasky /* try to get the preferred mode from EDID */ 1063278799Shselasky edid_parse(sc->sc_edid, &sc->sc_edid_info); 1064278799Shselasky#ifdef USB_DEBUG 1065278799Shselasky edid_print(&sc->sc_edid_info); 1066278799Shselasky#endif 1067278799Shselasky if (sc->sc_edid_info.edid_preferred_mode != NULL) { 1068278799Shselasky mode.hz = 1069278799Shselasky (sc->sc_edid_info.edid_preferred_mode->dot_clock * 1000) / 1070278799Shselasky (sc->sc_edid_info.edid_preferred_mode->htotal * 1071278799Shselasky sc->sc_edid_info.edid_preferred_mode->vtotal); 1072278799Shselasky mode.clock = 1073278799Shselasky sc->sc_edid_info.edid_preferred_mode->dot_clock / 10; 1074278799Shselasky mode.hdisplay = 1075278799Shselasky sc->sc_edid_info.edid_preferred_mode->hdisplay; 1076278799Shselasky mode.vdisplay = 1077278799Shselasky sc->sc_edid_info.edid_preferred_mode->vdisplay; 1078278799Shselasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, mode.hz, 1079278799Shselasky sc->sc_chip, mode.clock); 1080278799Shselasky sc->sc_cur_mode = index; 1081278799Shselasky } else { 1082278799Shselasky DPRINTF("no preferred mode found!\n"); 1083278799Shselasky } 1084278799Shselasky 1085278799Shselasky if (index == UDL_MAX_MODES) { 1086281815Shselasky DPRINTF("no mode line found\n"); 1087278799Shselasky 1088278799Shselasky i = 0; 1089278799Shselasky while (i < sc->sc_edid_info.edid_nmodes) { 1090278799Shselasky mode.hz = 1091278799Shselasky (sc->sc_edid_info.edid_modes[i].dot_clock * 1000) / 1092278799Shselasky (sc->sc_edid_info.edid_modes[i].htotal * 1093278799Shselasky sc->sc_edid_info.edid_modes[i].vtotal); 1094278799Shselasky mode.clock = 1095278799Shselasky sc->sc_edid_info.edid_modes[i].dot_clock / 10; 1096278799Shselasky mode.hdisplay = 1097278799Shselasky sc->sc_edid_info.edid_modes[i].hdisplay; 1098278799Shselasky mode.vdisplay = 1099278799Shselasky sc->sc_edid_info.edid_modes[i].vdisplay; 1100278799Shselasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, 1101278799Shselasky mode.hz, sc->sc_chip, mode.clock); 1102278799Shselasky if (index < UDL_MAX_MODES) 1103278799Shselasky if ((sc->sc_cur_mode == UDL_MAX_MODES) || 1104278799Shselasky (index > sc->sc_cur_mode)) 1105278799Shselasky sc->sc_cur_mode = index; 1106278799Shselasky i++; 1107278799Shselasky } 1108278799Shselasky } 1109278799Shselasky /* 1110278799Shselasky * If no mode found use default. 1111278799Shselasky */ 1112278799Shselasky if (sc->sc_cur_mode == UDL_MAX_MODES) 1113278799Shselasky sc->sc_cur_mode = udl_lookup_mode(800, 600, 60, sc->sc_chip, 0); 1114278799Shselasky} 1115278799Shselasky 1116278799Shselaskystatic int 1117278799Shselaskyudl_cmd_write_buf_le16(struct udl_softc *sc, const uint8_t *buf, uint32_t off, 1118278799Shselasky uint8_t pixels, int flags) 1119278799Shselasky{ 1120278799Shselasky struct udl_cmd_buf *cb; 1121278799Shselasky 1122278799Shselasky cb = udl_cmd_buf_alloc(sc, flags); 1123278799Shselasky if (cb == NULL) 1124278799Shselasky return (EAGAIN); 1125278799Shselasky 1126278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 1127278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 1128278799Shselasky udl_cmd_insert_int_3(cb, off); 1129278799Shselasky udl_cmd_insert_int_1(cb, pixels); 1130278799Shselasky udl_cmd_insert_buf_le16(cb, buf, 2 * pixels); 1131278799Shselasky udl_cmd_buf_send(sc, cb); 1132278799Shselasky 1133278799Shselasky return (0); 1134278799Shselasky} 1135278799Shselasky 1136278799Shselaskystatic int 1137278799Shselaskyudl_cmd_buf_copy_le16(struct udl_softc *sc, uint32_t src, uint32_t dst, 1138278799Shselasky uint8_t pixels, int flags) 1139278799Shselasky{ 1140278799Shselasky struct udl_cmd_buf *cb; 1141278799Shselasky 1142278799Shselasky cb = udl_cmd_buf_alloc(sc, flags); 1143278799Shselasky if (cb == NULL) 1144278799Shselasky return (EAGAIN); 1145278799Shselasky 1146278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 1147278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD); 1148278824Shselasky udl_cmd_insert_int_3(cb, dst); 1149278799Shselasky udl_cmd_insert_int_1(cb, pixels); 1150278824Shselasky udl_cmd_insert_int_3(cb, src); 1151278799Shselasky udl_cmd_buf_send(sc, cb); 1152278799Shselasky 1153278799Shselasky return (0); 1154278799Shselasky} 1155