udl.c revision 282725
1278799Shselasky/* $OpenBSD: udl.c,v 1.81 2014/12/09 07:05:06 doug Exp $ */ 2278799Shselasky/* $FreeBSD: head/sys/dev/usb/video/udl.c 282725 2015-05-10 12:45:21Z 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)}, 180278799Shselasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)} 181278799Shselasky}; 182278799Shselasky 183281644Shselaskystatic void 184281644Shselaskyudl_buffer_init(void *arg) 185281644Shselasky{ 186281644Shselasky mtx_init(&udl_buffer_mtx, "USB", "UDL", MTX_DEF); 187281644Shselasky TAILQ_INIT(&udl_buffer_head); 188281644Shselasky} 189281644ShselaskySYSINIT(udl_buffer_init, SI_SUB_LOCK, SI_ORDER_FIRST, udl_buffer_init, NULL); 190281644Shselasky 191281644ShselaskyCTASSERT(sizeof(struct udl_buffer) < PAGE_SIZE); 192281644Shselasky 193281644Shselaskystatic void * 194281644Shselaskyudl_buffer_alloc(uint32_t size) 195281644Shselasky{ 196281644Shselasky struct udl_buffer *buf; 197281644Shselasky mtx_lock(&udl_buffer_mtx); 198281644Shselasky TAILQ_FOREACH(buf, &udl_buffer_head, entry) { 199281644Shselasky if (buf->size == size) { 200281644Shselasky TAILQ_REMOVE(&udl_buffer_head, buf, entry); 201281644Shselasky break; 202281644Shselasky } 203281644Shselasky } 204281644Shselasky mtx_unlock(&udl_buffer_mtx); 205281644Shselasky if (buf != NULL) { 206282725Shselasky uint8_t *ptr = ((uint8_t *)buf) - size; 207281644Shselasky /* wipe and recycle buffer */ 208282725Shselasky memset(ptr, 0, size); 209282725Shselasky /* return buffer pointer */ 210282725Shselasky return (ptr); 211281644Shselasky } 212281644Shselasky /* allocate new buffer */ 213282725Shselasky return (malloc(size + sizeof(*buf), M_USB_DL, M_WAITOK | M_ZERO)); 214281644Shselasky} 215281644Shselasky 216281644Shselaskystatic void 217281644Shselaskyudl_buffer_free(void *_buf, uint32_t size) 218281644Shselasky{ 219281644Shselasky struct udl_buffer *buf; 220281644Shselasky 221282725Shselasky /* check for NULL pointer */ 222282725Shselasky if (_buf == NULL) 223281644Shselasky return; 224282725Shselasky /* compute pointer to recycle list */ 225282725Shselasky buf = (struct udl_buffer *)(((uint8_t *)_buf) + size); 226281644Shselasky 227281644Shselasky /* 228281644Shselasky * Memory mapped buffers should never be freed. 229281644Shselasky * Put display buffer into a recycle list. 230281644Shselasky */ 231281644Shselasky mtx_lock(&udl_buffer_mtx); 232281644Shselasky buf->size = size; 233281644Shselasky TAILQ_INSERT_TAIL(&udl_buffer_head, buf, entry); 234281644Shselasky mtx_unlock(&udl_buffer_mtx); 235281644Shselasky} 236281644Shselasky 237278799Shselaskystatic uint32_t 238278799Shselaskyudl_get_fb_size(struct udl_softc *sc) 239278799Shselasky{ 240278799Shselasky unsigned i = sc->sc_cur_mode; 241278799Shselasky 242278799Shselasky return ((uint32_t)udl_modes[i].hdisplay * 243278799Shselasky (uint32_t)udl_modes[i].vdisplay * 2); 244278799Shselasky} 245278799Shselasky 246278799Shselaskystatic uint32_t 247278799Shselaskyudl_get_fb_width(struct udl_softc *sc) 248278799Shselasky{ 249278799Shselasky unsigned i = sc->sc_cur_mode; 250278799Shselasky 251278851Shselasky return (udl_modes[i].hdisplay); 252278799Shselasky} 253278799Shselasky 254278799Shselaskystatic uint32_t 255278799Shselaskyudl_get_fb_height(struct udl_softc *sc) 256278799Shselasky{ 257278799Shselasky unsigned i = sc->sc_cur_mode; 258278799Shselasky 259278851Shselasky return (udl_modes[i].vdisplay); 260278799Shselasky} 261278799Shselasky 262278799Shselaskystatic uint32_t 263278799Shselaskyudl_get_fb_hz(struct udl_softc *sc) 264278799Shselasky{ 265278799Shselasky unsigned i = sc->sc_cur_mode; 266278799Shselasky 267278799Shselasky return (udl_modes[i].hz); 268278799Shselasky} 269278799Shselasky 270278799Shselaskystatic void 271278799Shselaskyudl_callout(void *arg) 272278799Shselasky{ 273278799Shselasky struct udl_softc *sc = arg; 274278799Shselasky const uint32_t max = udl_get_fb_size(sc); 275279753Shselasky int fps; 276278799Shselasky 277278799Shselasky if (sc->sc_power_save == 0) { 278279753Shselasky fps = udl_fps; 279279753Shselasky 280279753Shselasky /* figure out number of frames per second */ 281279753Shselasky if (fps < UDL_FPS_MIN) 282279753Shselasky fps = UDL_FPS_MIN; 283279753Shselasky else if (fps > UDL_FPS_MAX) 284279753Shselasky fps = UDL_FPS_MAX; 285279753Shselasky 286278799Shselasky if (sc->sc_sync_off >= max) 287278799Shselasky sc->sc_sync_off = 0; 288278799Shselasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 289278799Shselasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 290279753Shselasky } else { 291279753Shselasky fps = 1; 292278799Shselasky } 293279753Shselasky callout_reset(&sc->sc_callout, hz / fps, &udl_callout, sc); 294278799Shselasky} 295278799Shselasky 296278799Shselaskystatic int 297278799Shselaskyudl_probe(device_t dev) 298278799Shselasky{ 299278799Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 300278799Shselasky 301278799Shselasky if (uaa->usb_mode != USB_MODE_HOST) 302278799Shselasky return (ENXIO); 303278799Shselasky if (uaa->info.bConfigIndex != 0) 304278799Shselasky return (ENXIO); 305278799Shselasky if (uaa->info.bIfaceIndex != 0) 306278799Shselasky return (ENXIO); 307278799Shselasky 308278799Shselasky return (usbd_lookup_id_by_uaa(udl_devs, sizeof(udl_devs), uaa)); 309278799Shselasky} 310278799Shselasky 311278799Shselaskystatic int 312278799Shselaskyudl_attach(device_t dev) 313278799Shselasky{ 314278799Shselasky struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 315278799Shselasky struct sysctl_oid *tree = device_get_sysctl_tree(dev); 316278799Shselasky struct udl_softc *sc = device_get_softc(dev); 317278799Shselasky struct usb_attach_arg *uaa = device_get_ivars(dev); 318278799Shselasky int error; 319278799Shselasky int i; 320278799Shselasky 321278799Shselasky device_set_usb_desc(dev); 322278799Shselasky 323278799Shselasky mtx_init(&sc->sc_mtx, "UDL lock", NULL, MTX_DEF); 324278799Shselasky cv_init(&sc->sc_cv, "UDLCV"); 325278799Shselasky callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 326278799Shselasky sc->sc_udev = uaa->device; 327278799Shselasky 328278799Shselasky error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 329278799Shselasky sc->sc_xfer, udl_config, UDL_N_TRANSFER, sc, &sc->sc_mtx); 330278799Shselasky 331278799Shselasky if (error) { 332278799Shselasky DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error)); 333278799Shselasky goto detach; 334278799Shselasky } 335278799Shselasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_0], &sc->sc_xfer_head[0]); 336278799Shselasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_1], &sc->sc_xfer_head[1]); 337278799Shselasky 338278799Shselasky TAILQ_INIT(&sc->sc_xfer_head[0]); 339278799Shselasky TAILQ_INIT(&sc->sc_xfer_head[1]); 340278799Shselasky TAILQ_INIT(&sc->sc_cmd_buf_free); 341278799Shselasky TAILQ_INIT(&sc->sc_cmd_buf_pending); 342278799Shselasky 343278799Shselasky sc->sc_def_chip = -1; 344278799Shselasky sc->sc_chip = USB_GET_DRIVER_INFO(uaa); 345278799Shselasky sc->sc_def_mode = -1; 346278799Shselasky sc->sc_cur_mode = UDL_MAX_MODES; 347278799Shselasky 348278799Shselasky /* Allow chip ID to be overwritten */ 349278799Shselasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid_force", 350278799Shselasky CTLFLAG_RWTUN, &sc->sc_def_chip, 0, "chip ID"); 351278799Shselasky 352278799Shselasky /* Export current chip ID */ 353278799Shselasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid", 354278799Shselasky CTLFLAG_RD, &sc->sc_chip, 0, "chip ID"); 355278799Shselasky 356278799Shselasky if (sc->sc_def_chip > -1 && sc->sc_def_chip <= DLMAX) { 357278799Shselasky device_printf(dev, "Forcing chip ID to 0x%04x\n", sc->sc_def_chip); 358278799Shselasky sc->sc_chip = sc->sc_def_chip; 359278799Shselasky } 360278799Shselasky /* 361278799Shselasky * The product might have more than one chip 362278799Shselasky */ 363278799Shselasky if (sc->sc_chip == DLUNK) 364278799Shselasky udl_select_chip(sc, uaa); 365278799Shselasky 366278799Shselasky for (i = 0; i != UDL_CMD_MAX_BUFFERS; i++) { 367278799Shselasky struct udl_cmd_buf *cb = &sc->sc_cmd_buf_temp[i]; 368278799Shselasky 369278799Shselasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 370278799Shselasky } 371278799Shselasky 372278799Shselasky /* 373278799Shselasky * Initialize chip. 374278799Shselasky */ 375278799Shselasky error = udl_init_chip(sc); 376278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 377278799Shselasky goto detach; 378278799Shselasky 379278799Shselasky /* 380278799Shselasky * Select edid mode. 381278799Shselasky */ 382278799Shselasky udl_select_mode(sc); 383278799Shselasky 384278799Shselasky /* Allow default mode to be overwritten */ 385278799Shselasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode_force", 386278799Shselasky CTLFLAG_RWTUN, &sc->sc_def_mode, 0, "mode"); 387278799Shselasky 388278799Shselasky /* Export current mode */ 389278799Shselasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode", 390278799Shselasky CTLFLAG_RD, &sc->sc_cur_mode, 0, "mode"); 391278799Shselasky 392278799Shselasky i = sc->sc_def_mode; 393278799Shselasky if (i > -1 && i < UDL_MAX_MODES) { 394278799Shselasky if (udl_modes[i].chip <= sc->sc_chip) { 395278799Shselasky device_printf(dev, "Forcing mode to %d\n", i); 396278799Shselasky sc->sc_cur_mode = i; 397278799Shselasky } 398278799Shselasky } 399278799Shselasky /* Printout current mode */ 400278799Shselasky device_printf(dev, "Mode selected %dx%d @ %dHz\n", 401278799Shselasky (int)udl_get_fb_width(sc), 402278799Shselasky (int)udl_get_fb_height(sc), 403278799Shselasky (int)udl_get_fb_hz(sc)); 404278799Shselasky 405278799Shselasky udl_init_resolution(sc); 406278799Shselasky 407278799Shselasky /* Allocate frame buffer */ 408278799Shselasky udl_fbmem_alloc(sc); 409278799Shselasky 410278799Shselasky UDL_LOCK(sc); 411278799Shselasky udl_callout(sc); 412278799Shselasky UDL_UNLOCK(sc); 413278799Shselasky 414278799Shselasky sc->sc_fb_info.fb_name = device_get_nameunit(dev); 415278799Shselasky sc->sc_fb_info.fb_size = sc->sc_fb_size; 416278799Shselasky sc->sc_fb_info.fb_bpp = 16; 417278799Shselasky sc->sc_fb_info.fb_depth = 16; 418278799Shselasky sc->sc_fb_info.fb_width = udl_get_fb_width(sc); 419278799Shselasky sc->sc_fb_info.fb_height = udl_get_fb_height(sc); 420278799Shselasky sc->sc_fb_info.fb_stride = sc->sc_fb_info.fb_width * 2; 421278824Shselasky sc->sc_fb_info.fb_pbase = 0; 422278799Shselasky sc->sc_fb_info.fb_vbase = (uintptr_t)sc->sc_fb_addr; 423278847Shselasky sc->sc_fb_info.fb_priv = sc; 424278847Shselasky sc->sc_fb_info.setblankmode = &udl_fb_setblankmode; 425278799Shselasky 426278799Shselasky sc->sc_fbdev = device_add_child(dev, "fbd", -1); 427278799Shselasky if (sc->sc_fbdev == NULL) 428278799Shselasky goto detach; 429278799Shselasky if (device_probe_and_attach(sc->sc_fbdev) != 0) 430278799Shselasky goto detach; 431278799Shselasky 432278799Shselasky return (0); 433278799Shselasky 434278799Shselaskydetach: 435278799Shselasky udl_detach(dev); 436278799Shselasky 437278799Shselasky return (ENXIO); 438278799Shselasky} 439278799Shselasky 440278799Shselaskystatic int 441278799Shselaskyudl_detach(device_t dev) 442278799Shselasky{ 443278799Shselasky struct udl_softc *sc = device_get_softc(dev); 444278799Shselasky 445278799Shselasky if (sc->sc_fbdev != NULL) { 446278799Shselasky device_t bdev; 447278799Shselasky 448278799Shselasky bdev = sc->sc_fbdev; 449278799Shselasky sc->sc_fbdev = NULL; 450278799Shselasky device_detach(bdev); 451278799Shselasky device_delete_child(dev, bdev); 452278799Shselasky } 453278799Shselasky UDL_LOCK(sc); 454278799Shselasky sc->sc_gone = 1; 455278799Shselasky callout_stop(&sc->sc_callout); 456278799Shselasky UDL_UNLOCK(sc); 457278799Shselasky 458278799Shselasky usbd_transfer_unsetup(sc->sc_xfer, UDL_N_TRANSFER); 459278799Shselasky 460278799Shselasky callout_drain(&sc->sc_callout); 461278799Shselasky 462278799Shselasky mtx_destroy(&sc->sc_mtx); 463278799Shselasky cv_destroy(&sc->sc_cv); 464278799Shselasky 465281644Shselasky /* put main framebuffer into a recycle list, if any */ 466281644Shselasky udl_buffer_free(sc->sc_fb_addr, sc->sc_fb_size); 467278799Shselasky 468281644Shselasky /* free shadow framebuffer memory, if any */ 469281644Shselasky free(sc->sc_fb_copy, M_USB_DL); 470281644Shselasky 471278799Shselasky return (0); 472278799Shselasky} 473278799Shselasky 474278799Shselaskystatic struct fb_info * 475278799Shselaskyudl_fb_getinfo(device_t dev) 476278799Shselasky{ 477278799Shselasky struct udl_softc *sc = device_get_softc(dev); 478278799Shselasky 479278799Shselasky return (&sc->sc_fb_info); 480278799Shselasky} 481278799Shselasky 482278799Shselaskystatic int 483278847Shselaskyudl_fb_setblankmode(void *arg, int mode) 484278799Shselasky{ 485278847Shselasky struct udl_softc *sc = arg; 486278799Shselasky 487278799Shselasky switch (mode) { 488278799Shselasky case V_DISPLAY_ON: 489278799Shselasky udl_power_save(sc, 1, M_WAITOK); 490278799Shselasky break; 491278799Shselasky case V_DISPLAY_BLANK: 492278799Shselasky udl_power_save(sc, 1, M_WAITOK); 493278799Shselasky if (sc->sc_fb_addr != 0) { 494278799Shselasky const uint32_t max = udl_get_fb_size(sc); 495278799Shselasky 496278799Shselasky memset((void *)sc->sc_fb_addr, 0, max); 497278799Shselasky } 498278799Shselasky break; 499278799Shselasky case V_DISPLAY_STAND_BY: 500278799Shselasky case V_DISPLAY_SUSPEND: 501278799Shselasky udl_power_save(sc, 0, M_WAITOK); 502278799Shselasky break; 503278799Shselasky } 504278799Shselasky return (0); 505278799Shselasky} 506278799Shselasky 507278799Shselaskystatic struct udl_cmd_buf * 508278852Shselaskyudl_cmd_buf_alloc_locked(struct udl_softc *sc, int flags) 509278799Shselasky{ 510278799Shselasky struct udl_cmd_buf *cb; 511278799Shselasky 512278799Shselasky while ((cb = TAILQ_FIRST(&sc->sc_cmd_buf_free)) == NULL) { 513278799Shselasky if (flags != M_WAITOK) 514278799Shselasky break; 515278799Shselasky cv_wait(&sc->sc_cv, &sc->sc_mtx); 516278799Shselasky } 517278799Shselasky if (cb != NULL) { 518278799Shselasky TAILQ_REMOVE(&sc->sc_cmd_buf_free, cb, entry); 519278799Shselasky cb->off = 0; 520278799Shselasky } 521278852Shselasky return (cb); 522278852Shselasky} 523278852Shselasky 524278852Shselaskystatic struct udl_cmd_buf * 525278852Shselaskyudl_cmd_buf_alloc(struct udl_softc *sc, int flags) 526278852Shselasky{ 527278852Shselasky struct udl_cmd_buf *cb; 528278852Shselasky 529278852Shselasky UDL_LOCK(sc); 530278852Shselasky cb = udl_cmd_buf_alloc_locked(sc, flags); 531278799Shselasky UDL_UNLOCK(sc); 532278799Shselasky return (cb); 533278799Shselasky} 534278799Shselasky 535278799Shselaskystatic void 536278799Shselaskyudl_cmd_buf_send(struct udl_softc *sc, struct udl_cmd_buf *cb) 537278799Shselasky{ 538278799Shselasky UDL_LOCK(sc); 539278799Shselasky if (sc->sc_gone) { 540278799Shselasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 541278799Shselasky } else { 542278799Shselasky /* mark end of command stack */ 543278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 544278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_EOC); 545278799Shselasky 546278799Shselasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_pending, cb, entry); 547278799Shselasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 548278799Shselasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 549278799Shselasky } 550278799Shselasky UDL_UNLOCK(sc); 551278799Shselasky} 552278799Shselasky 553278799Shselaskystatic struct udl_cmd_buf * 554278852Shselaskyudl_fb_synchronize_locked(struct udl_softc *sc) 555278799Shselasky{ 556278799Shselasky const uint32_t max = udl_get_fb_size(sc); 557278799Shselasky 558278824Shselasky /* check if framebuffer is not ready */ 559278824Shselasky if (sc->sc_fb_addr == NULL || 560278824Shselasky sc->sc_fb_copy == NULL) 561278824Shselasky return (NULL); 562278824Shselasky 563278799Shselasky while (sc->sc_sync_off < max) { 564278799Shselasky uint32_t delta = max - sc->sc_sync_off; 565278799Shselasky 566278799Shselasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 567278799Shselasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 568278799Shselasky if (bcmp(sc->sc_fb_addr + sc->sc_sync_off, sc->sc_fb_copy + sc->sc_sync_off, delta) != 0) { 569278799Shselasky struct udl_cmd_buf *cb; 570278799Shselasky 571278852Shselasky cb = udl_cmd_buf_alloc_locked(sc, M_NOWAIT); 572278799Shselasky if (cb == NULL) 573278799Shselasky goto done; 574278799Shselasky memcpy(sc->sc_fb_copy + sc->sc_sync_off, 575278799Shselasky sc->sc_fb_addr + sc->sc_sync_off, delta); 576278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 577278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 578278799Shselasky udl_cmd_insert_int_3(cb, sc->sc_sync_off); 579278799Shselasky udl_cmd_insert_int_1(cb, delta / 2); 580278799Shselasky udl_cmd_insert_buf_le16(cb, sc->sc_fb_copy + sc->sc_sync_off, delta); 581278799Shselasky sc->sc_sync_off += delta; 582278799Shselasky return (cb); 583278799Shselasky } else { 584278799Shselasky sc->sc_sync_off += delta; 585278799Shselasky } 586278799Shselasky } 587278799Shselaskydone: 588278799Shselasky return (NULL); 589278799Shselasky} 590278799Shselasky 591278799Shselaskystatic void 592278799Shselaskyudl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 593278799Shselasky{ 594278799Shselasky struct udl_softc *sc = usbd_xfer_softc(xfer); 595278799Shselasky struct udl_cmd_head *phead = usbd_xfer_get_priv(xfer); 596278799Shselasky struct udl_cmd_buf *cb; 597278799Shselasky unsigned i; 598278799Shselasky 599278799Shselasky switch (USB_GET_STATE(xfer)) { 600278799Shselasky case USB_ST_TRANSFERRED: 601278799Shselasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 602278799Shselasky case USB_ST_SETUP: 603278799Shselaskytr_setup: 604278799Shselasky for (i = 0; i != UDL_CMD_MAX_FRAMES; i++) { 605278799Shselasky cb = TAILQ_FIRST(&sc->sc_cmd_buf_pending); 606278799Shselasky if (cb == NULL) { 607278852Shselasky cb = udl_fb_synchronize_locked(sc); 608278799Shselasky if (cb == NULL) 609278799Shselasky break; 610278824Shselasky } else { 611278824Shselasky TAILQ_REMOVE(&sc->sc_cmd_buf_pending, cb, entry); 612278799Shselasky } 613278799Shselasky TAILQ_INSERT_TAIL(phead, cb, entry); 614278799Shselasky usbd_xfer_set_frame_data(xfer, i, cb->buf, cb->off); 615278799Shselasky } 616278799Shselasky if (i != 0) { 617278799Shselasky usbd_xfer_set_frames(xfer, i); 618278799Shselasky usbd_transfer_submit(xfer); 619278799Shselasky } 620278799Shselasky break; 621278799Shselasky default: 622278799Shselasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 623278799Shselasky if (error != USB_ERR_CANCELLED) { 624278799Shselasky /* try clear stall first */ 625278799Shselasky usbd_xfer_set_stall(xfer); 626278799Shselasky goto tr_setup; 627278799Shselasky } 628278799Shselasky break; 629278799Shselasky } 630278799Shselasky /* wakeup any waiters */ 631278799Shselasky cv_signal(&sc->sc_cv); 632278799Shselasky} 633278799Shselasky 634278799Shselaskystatic int 635278799Shselaskyudl_power_save(struct udl_softc *sc, int on, int flags) 636278799Shselasky{ 637278799Shselasky struct udl_cmd_buf *cb; 638278799Shselasky 639278799Shselasky /* get new buffer */ 640278799Shselasky cb = udl_cmd_buf_alloc(sc, flags); 641278799Shselasky if (cb == NULL) 642278799Shselasky return (EAGAIN); 643278799Shselasky 644278799Shselasky DPRINTF("screen %s\n", on ? "ON" : "OFF"); 645278799Shselasky 646278799Shselasky sc->sc_power_save = on ? 0 : 1; 647278799Shselasky 648278799Shselasky if (on) 649278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 650278799Shselasky else 651278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF); 652278799Shselasky 653278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 654278799Shselasky udl_cmd_buf_send(sc, cb); 655278799Shselasky return (0); 656278799Shselasky} 657278799Shselasky 658278799Shselaskystatic int 659278799Shselaskyudl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r, 660278799Shselasky uint16_t index, uint16_t value, uint8_t *buf, size_t len) 661278799Shselasky{ 662278799Shselasky usb_device_request_t req; 663278799Shselasky int error; 664278799Shselasky 665278799Shselasky req.bmRequestType = rt; 666278799Shselasky req.bRequest = r; 667278799Shselasky USETW(req.wIndex, index); 668278799Shselasky USETW(req.wValue, value); 669278799Shselasky USETW(req.wLength, len); 670278799Shselasky 671278799Shselasky error = usbd_do_request_flags(sc->sc_udev, NULL, 672278799Shselasky &req, buf, 0, NULL, USB_DEFAULT_TIMEOUT); 673278799Shselasky 674278799Shselasky DPRINTF("%s\n", usbd_errstr(error)); 675278799Shselasky 676278799Shselasky return (error); 677278799Shselasky} 678278799Shselasky 679278799Shselaskystatic int 680278799Shselaskyudl_poll(struct udl_softc *sc, uint32_t *buf) 681278799Shselasky{ 682278799Shselasky uint32_t lbuf; 683278799Shselasky int error; 684278799Shselasky 685278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 686278799Shselasky UDL_CTRL_CMD_POLL, 0x0000, 0x0000, (uint8_t *)&lbuf, sizeof(lbuf)); 687278799Shselasky if (error == USB_ERR_NORMAL_COMPLETION) 688278799Shselasky *buf = le32toh(lbuf); 689278799Shselasky return (error); 690278799Shselasky} 691278799Shselasky 692278799Shselaskystatic int 693278799Shselaskyudl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf) 694278799Shselasky{ 695278799Shselasky uint8_t lbuf[1]; 696278799Shselasky int error; 697278799Shselasky 698278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 699278799Shselasky UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1); 700278799Shselasky if (error == USB_ERR_NORMAL_COMPLETION) 701278799Shselasky *buf = *(uint8_t *)lbuf; 702278799Shselasky return (error); 703278799Shselasky} 704278799Shselasky 705278799Shselaskystatic int 706278799Shselaskyudl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf) 707278799Shselasky{ 708278799Shselasky int error; 709278799Shselasky 710278799Shselasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 711278799Shselasky UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1); 712278799Shselasky return (error); 713278799Shselasky} 714278799Shselasky 715278799Shselaskystatic int 716278799Shselaskyudl_read_edid(struct udl_softc *sc, uint8_t *buf) 717278799Shselasky{ 718278799Shselasky uint8_t lbuf[64]; 719278799Shselasky uint16_t offset; 720278799Shselasky int error; 721278799Shselasky 722278799Shselasky offset = 0; 723278799Shselasky 724278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 725278799Shselasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 726278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 727278799Shselasky goto fail; 728278799Shselasky bcopy(lbuf + 1, buf + offset, 63); 729278799Shselasky offset += 63; 730278799Shselasky 731278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 732278799Shselasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 733278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 734278799Shselasky goto fail; 735278799Shselasky bcopy(lbuf + 1, buf + offset, 63); 736278799Shselasky offset += 63; 737278799Shselasky 738278799Shselasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 739278799Shselasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3); 740278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 741278799Shselasky goto fail; 742278799Shselasky bcopy(lbuf + 1, buf + offset, 2); 743278799Shselaskyfail: 744278799Shselasky return (error); 745278799Shselasky} 746278799Shselasky 747278799Shselaskystatic uint8_t 748278799Shselaskyudl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t hz, 749278799Shselasky uint16_t chip, uint32_t clock) 750278799Shselasky{ 751278799Shselasky uint8_t idx; 752278799Shselasky 753278799Shselasky /* 754278799Shselasky * Check first if we have a matching mode with pixelclock 755278799Shselasky */ 756278799Shselasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 757278799Shselasky if ((udl_modes[idx].hdisplay == hdisplay) && 758278799Shselasky (udl_modes[idx].vdisplay == vdisplay) && 759278799Shselasky (udl_modes[idx].clock == clock) && 760278799Shselasky (udl_modes[idx].chip <= chip)) { 761278799Shselasky return (idx); 762278799Shselasky } 763278799Shselasky } 764278799Shselasky 765278799Shselasky /* 766278799Shselasky * If not, check for matching mode with update frequency 767278799Shselasky */ 768278799Shselasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 769278799Shselasky if ((udl_modes[idx].hdisplay == hdisplay) && 770278799Shselasky (udl_modes[idx].vdisplay == vdisplay) && 771278799Shselasky (udl_modes[idx].hz == hz) && 772278799Shselasky (udl_modes[idx].chip <= chip)) { 773278799Shselasky return (idx); 774278799Shselasky } 775278799Shselasky } 776278799Shselasky return (idx); 777278799Shselasky} 778278799Shselasky 779278799Shselaskystatic void 780278799Shselaskyudl_select_chip(struct udl_softc *sc, struct usb_attach_arg *uaa) 781278799Shselasky{ 782278799Shselasky const char *pserial; 783278799Shselasky 784278799Shselasky pserial = usb_get_serial(uaa->device); 785278799Shselasky 786278799Shselasky sc->sc_chip = DL120; 787278799Shselasky 788278799Shselasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 789278799Shselasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_WSDVI)) { 790278799Shselasky 791278799Shselasky /* 792278799Shselasky * WS Tech DVI is DL120 or DL160. All deviced uses the 793278799Shselasky * same revision (0.04) so iSerialNumber must be used 794278799Shselasky * to determin which chip it is. 795278799Shselasky */ 796278799Shselasky 797278799Shselasky if (strlen(pserial) > 7) { 798278799Shselasky if (strncmp(pserial, "0198-13", 7) == 0) 799278799Shselasky sc->sc_chip = DL160; 800278799Shselasky } 801278799Shselasky DPRINTF("iSerialNumber (%s) used to select chip (%d)\n", 802278799Shselasky pserial, sc->sc_chip); 803278799Shselasky } 804278799Shselasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 805278799Shselasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_SWDVI)) { 806278799Shselasky 807278799Shselasky /* 808278799Shselasky * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision 809278799Shselasky * can be used to differ between DL1x0 and DL1x5. Minor to 810278799Shselasky * differ between DL1x5. iSerialNumber seems not to be uniqe. 811278799Shselasky */ 812278799Shselasky 813278799Shselasky sc->sc_chip = DL160; 814278799Shselasky 815278799Shselasky if (uaa->info.bcdDevice >= 0x100) { 816278799Shselasky sc->sc_chip = DL165; 817278799Shselasky if (uaa->info.bcdDevice == 0x104) 818278799Shselasky sc->sc_chip = DL195; 819278799Shselasky if (uaa->info.bcdDevice == 0x108) 820278799Shselasky sc->sc_chip = DL125; 821278799Shselasky } 822278799Shselasky DPRINTF("bcdDevice (%02x) used to select chip (%d)\n", 823278799Shselasky uaa->info.bcdDevice, sc->sc_chip); 824278799Shselasky } 825278799Shselasky} 826278799Shselasky 827278799Shselaskystatic int 828278799Shselaskyudl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len) 829278799Shselasky{ 830278799Shselasky int error; 831278799Shselasky 832278799Shselasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 833278799Shselasky UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len); 834278799Shselasky return (error); 835278799Shselasky} 836278799Shselasky 837278799Shselaskystatic void 838278799Shselaskyudl_fbmem_alloc(struct udl_softc *sc) 839278799Shselasky{ 840278799Shselasky uint32_t size; 841278799Shselasky 842278799Shselasky size = udl_get_fb_size(sc); 843278799Shselasky size = round_page(size); 844281644Shselasky /* check for zero size */ 845281644Shselasky if (size == 0) 846281644Shselasky size = PAGE_SIZE; 847279753Shselasky /* 848279753Shselasky * It is assumed that allocations above PAGE_SIZE bytes will 849279753Shselasky * be PAGE_SIZE aligned for use with mmap() 850279753Shselasky */ 851281644Shselasky sc->sc_fb_addr = udl_buffer_alloc(size); 852281644Shselasky sc->sc_fb_copy = malloc(size, M_USB_DL, M_WAITOK | M_ZERO); 853278799Shselasky sc->sc_fb_size = size; 854278799Shselasky} 855278799Shselasky 856278799Shselaskystatic void 857278799Shselaskyudl_cmd_insert_int_1(struct udl_cmd_buf *cb, uint8_t value) 858278799Shselasky{ 859278799Shselasky 860278799Shselasky cb->buf[cb->off] = value; 861278799Shselasky cb->off += 1; 862278799Shselasky} 863278799Shselasky 864278799Shselasky#if 0 865278799Shselaskystatic void 866278799Shselaskyudl_cmd_insert_int_2(struct udl_cmd_buf *cb, uint16_t value) 867278799Shselasky{ 868278799Shselasky uint16_t lvalue; 869278799Shselasky 870278799Shselasky lvalue = htobe16(value); 871278799Shselasky bcopy(&lvalue, cb->buf + cb->off, 2); 872278799Shselasky 873278799Shselasky cb->off += 2; 874278799Shselasky} 875278799Shselasky 876278799Shselasky#endif 877278799Shselasky 878278799Shselaskystatic void 879278799Shselaskyudl_cmd_insert_int_3(struct udl_cmd_buf *cb, uint32_t value) 880278799Shselasky{ 881278799Shselasky uint32_t lvalue; 882278799Shselasky 883278799Shselasky#if BYTE_ORDER == BIG_ENDIAN 884278799Shselasky lvalue = htobe32(value) << 8; 885278799Shselasky#else 886278799Shselasky lvalue = htobe32(value) >> 8; 887278799Shselasky#endif 888278799Shselasky bcopy(&lvalue, cb->buf + cb->off, 3); 889278799Shselasky 890278799Shselasky cb->off += 3; 891278799Shselasky} 892278799Shselasky 893278799Shselasky#if 0 894278799Shselaskystatic void 895278799Shselaskyudl_cmd_insert_int_4(struct udl_cmd_buf *cb, uint32_t value) 896278799Shselasky{ 897278799Shselasky uint32_t lvalue; 898278799Shselasky 899278799Shselasky lvalue = htobe32(value); 900278799Shselasky bcopy(&lvalue, cb->buf + cb->off, 4); 901278799Shselasky 902278799Shselasky cb->off += 4; 903278799Shselasky} 904278799Shselasky 905278799Shselasky#endif 906278799Shselasky 907278799Shselaskystatic void 908278799Shselaskyudl_cmd_insert_buf_le16(struct udl_cmd_buf *cb, const uint8_t *buf, uint32_t len) 909278799Shselasky{ 910278799Shselasky uint32_t x; 911278799Shselasky 912278799Shselasky for (x = 0; x != len; x += 2) { 913278799Shselasky /* byte swap from little endian to big endian */ 914278799Shselasky cb->buf[cb->off + x + 0] = buf[x + 1]; 915278799Shselasky cb->buf[cb->off + x + 1] = buf[x + 0]; 916278799Shselasky } 917278799Shselasky cb->off += len; 918278799Shselasky} 919278799Shselasky 920278799Shselaskystatic void 921278799Shselaskyudl_cmd_write_reg_1(struct udl_cmd_buf *cb, uint8_t reg, uint8_t val) 922278799Shselasky{ 923278799Shselasky 924278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 925278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_REG_WRITE_1); 926278799Shselasky udl_cmd_insert_int_1(cb, reg); 927278799Shselasky udl_cmd_insert_int_1(cb, val); 928278799Shselasky} 929278799Shselasky 930278799Shselaskystatic void 931278799Shselaskyudl_cmd_write_reg_3(struct udl_cmd_buf *cb, uint8_t reg, uint32_t val) 932278799Shselasky{ 933278799Shselasky 934278799Shselasky udl_cmd_write_reg_1(cb, reg + 0, (val >> 16) & 0xff); 935278799Shselasky udl_cmd_write_reg_1(cb, reg + 1, (val >> 8) & 0xff); 936278799Shselasky udl_cmd_write_reg_1(cb, reg + 2, (val >> 0) & 0xff); 937278799Shselasky} 938278799Shselasky 939278799Shselaskystatic int 940278799Shselaskyudl_init_chip(struct udl_softc *sc) 941278799Shselasky{ 942278799Shselasky uint32_t ui32; 943278799Shselasky uint8_t ui8; 944278799Shselasky int error; 945278799Shselasky 946278799Shselasky error = udl_poll(sc, &ui32); 947278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 948278799Shselasky return (error); 949278799Shselasky DPRINTF("poll=0x%08x\n", ui32); 950278799Shselasky 951278799Shselasky /* Some products may use later chip too */ 952278799Shselasky switch (ui32 & 0xff) { 953278799Shselasky case 0xf1: /* DL1x5 */ 954278799Shselasky switch (sc->sc_chip) { 955278799Shselasky case DL120: 956278799Shselasky sc->sc_chip = DL125; 957278799Shselasky break; 958278799Shselasky case DL160: 959278799Shselasky sc->sc_chip = DL165; 960278799Shselasky break; 961278799Shselasky } 962278799Shselasky break; 963278799Shselasky } 964278799Shselasky DPRINTF("chip 0x%04x\n", sc->sc_chip); 965278799Shselasky 966278799Shselasky error = udl_read_1(sc, 0xc484, &ui8); 967278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 968278799Shselasky return (error); 969278799Shselasky DPRINTF("read 0x%02x from 0xc484\n", ui8); 970278799Shselasky 971278799Shselasky error = udl_write_1(sc, 0xc41f, 0x01); 972278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 973278799Shselasky return (error); 974278799Shselasky DPRINTF("write 0x01 to 0xc41f\n"); 975278799Shselasky 976278799Shselasky error = udl_read_edid(sc, sc->sc_edid); 977278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 978278799Shselasky return (error); 979278799Shselasky DPRINTF("read EDID\n"); 980278799Shselasky 981278799Shselasky error = udl_set_enc_key(sc, __DECONST(void *, udl_null_key_1), 982278799Shselasky sizeof(udl_null_key_1)); 983278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 984278799Shselasky return (error); 985278799Shselasky DPRINTF("set encryption key\n"); 986278799Shselasky 987278799Shselasky error = udl_write_1(sc, 0xc40b, 0x00); 988278799Shselasky if (error != USB_ERR_NORMAL_COMPLETION) 989278799Shselasky return (error); 990278799Shselasky DPRINTF("write 0x00 to 0xc40b\n"); 991278799Shselasky 992278799Shselasky return (USB_ERR_NORMAL_COMPLETION); 993278799Shselasky} 994278799Shselasky 995278799Shselaskystatic void 996278799Shselaskyudl_init_fb_offsets(struct udl_cmd_buf *cb, uint32_t start16, uint32_t stride16, 997278799Shselasky uint32_t start8, uint32_t stride8) 998278799Shselasky{ 999278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 1000278799Shselasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START16, start16); 1001278799Shselasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE16, stride16); 1002278799Shselasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START8, start8); 1003278799Shselasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE8, stride8); 1004278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 1005278799Shselasky} 1006278799Shselasky 1007278799Shselaskystatic int 1008278799Shselaskyudl_init_resolution(struct udl_softc *sc) 1009278799Shselasky{ 1010278799Shselasky const uint32_t max = udl_get_fb_size(sc); 1011278799Shselasky const uint8_t *buf = udl_modes[sc->sc_cur_mode].mode; 1012278799Shselasky struct udl_cmd_buf *cb; 1013278799Shselasky uint32_t delta; 1014278799Shselasky uint32_t i; 1015278799Shselasky int error; 1016278799Shselasky 1017278799Shselasky /* get new buffer */ 1018278799Shselasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 1019278799Shselasky if (cb == NULL) 1020278799Shselasky return (EAGAIN); 1021278799Shselasky 1022278799Shselasky /* write resolution values and set video memory offsets */ 1023278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 1024278799Shselasky for (i = 0; i < UDL_MODE_SIZE; i++) 1025278799Shselasky udl_cmd_write_reg_1(cb, i, buf[i]); 1026278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 1027278799Shselasky 1028278799Shselasky udl_init_fb_offsets(cb, 0x000000, 0x000a00, 0x555555, 0x000500); 1029278799Shselasky udl_cmd_buf_send(sc, cb); 1030278799Shselasky 1031278799Shselasky /* fill screen with black color */ 1032278799Shselasky for (i = 0; i < max; i += delta) { 1033278799Shselasky static const uint8_t udl_black[UDL_CMD_MAX_PIXEL_COUNT * 2] __aligned(4); 1034278799Shselasky 1035278799Shselasky delta = max - i; 1036278799Shselasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 1037278799Shselasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 1038278799Shselasky if (i == 0) 1039278799Shselasky error = udl_cmd_write_buf_le16(sc, udl_black, i, delta / 2, M_WAITOK); 1040278799Shselasky else 1041278799Shselasky error = udl_cmd_buf_copy_le16(sc, 0, i, delta / 2, M_WAITOK); 1042278799Shselasky if (error) 1043278799Shselasky return (error); 1044278799Shselasky } 1045278799Shselasky 1046278799Shselasky /* get new buffer */ 1047278799Shselasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 1048278799Shselasky if (cb == NULL) 1049278799Shselasky return (EAGAIN); 1050278799Shselasky 1051278799Shselasky /* show framebuffer content */ 1052278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 1053278799Shselasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 1054278799Shselasky udl_cmd_buf_send(sc, cb); 1055278799Shselasky return (0); 1056278799Shselasky} 1057278799Shselasky 1058278799Shselaskystatic void 1059278799Shselaskyudl_select_mode(struct udl_softc *sc) 1060278799Shselasky{ 1061278799Shselasky struct udl_mode mode; 1062278799Shselasky int index = UDL_MAX_MODES; 1063278799Shselasky int i; 1064278799Shselasky 1065278799Shselasky /* try to get the preferred mode from EDID */ 1066278799Shselasky edid_parse(sc->sc_edid, &sc->sc_edid_info); 1067278799Shselasky#ifdef USB_DEBUG 1068278799Shselasky edid_print(&sc->sc_edid_info); 1069278799Shselasky#endif 1070278799Shselasky if (sc->sc_edid_info.edid_preferred_mode != NULL) { 1071278799Shselasky mode.hz = 1072278799Shselasky (sc->sc_edid_info.edid_preferred_mode->dot_clock * 1000) / 1073278799Shselasky (sc->sc_edid_info.edid_preferred_mode->htotal * 1074278799Shselasky sc->sc_edid_info.edid_preferred_mode->vtotal); 1075278799Shselasky mode.clock = 1076278799Shselasky sc->sc_edid_info.edid_preferred_mode->dot_clock / 10; 1077278799Shselasky mode.hdisplay = 1078278799Shselasky sc->sc_edid_info.edid_preferred_mode->hdisplay; 1079278799Shselasky mode.vdisplay = 1080278799Shselasky sc->sc_edid_info.edid_preferred_mode->vdisplay; 1081278799Shselasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, mode.hz, 1082278799Shselasky sc->sc_chip, mode.clock); 1083278799Shselasky sc->sc_cur_mode = index; 1084278799Shselasky } else { 1085278799Shselasky DPRINTF("no preferred mode found!\n"); 1086278799Shselasky } 1087278799Shselasky 1088278799Shselasky if (index == UDL_MAX_MODES) { 1089281815Shselasky DPRINTF("no mode line found\n"); 1090278799Shselasky 1091278799Shselasky i = 0; 1092278799Shselasky while (i < sc->sc_edid_info.edid_nmodes) { 1093278799Shselasky mode.hz = 1094278799Shselasky (sc->sc_edid_info.edid_modes[i].dot_clock * 1000) / 1095278799Shselasky (sc->sc_edid_info.edid_modes[i].htotal * 1096278799Shselasky sc->sc_edid_info.edid_modes[i].vtotal); 1097278799Shselasky mode.clock = 1098278799Shselasky sc->sc_edid_info.edid_modes[i].dot_clock / 10; 1099278799Shselasky mode.hdisplay = 1100278799Shselasky sc->sc_edid_info.edid_modes[i].hdisplay; 1101278799Shselasky mode.vdisplay = 1102278799Shselasky sc->sc_edid_info.edid_modes[i].vdisplay; 1103278799Shselasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, 1104278799Shselasky mode.hz, sc->sc_chip, mode.clock); 1105278799Shselasky if (index < UDL_MAX_MODES) 1106278799Shselasky if ((sc->sc_cur_mode == UDL_MAX_MODES) || 1107278799Shselasky (index > sc->sc_cur_mode)) 1108278799Shselasky sc->sc_cur_mode = index; 1109278799Shselasky i++; 1110278799Shselasky } 1111278799Shselasky } 1112278799Shselasky /* 1113278799Shselasky * If no mode found use default. 1114278799Shselasky */ 1115278799Shselasky if (sc->sc_cur_mode == UDL_MAX_MODES) 1116278799Shselasky sc->sc_cur_mode = udl_lookup_mode(800, 600, 60, sc->sc_chip, 0); 1117278799Shselasky} 1118278799Shselasky 1119278799Shselaskystatic int 1120278799Shselaskyudl_cmd_write_buf_le16(struct udl_softc *sc, const uint8_t *buf, uint32_t off, 1121278799Shselasky uint8_t pixels, int flags) 1122278799Shselasky{ 1123278799Shselasky struct udl_cmd_buf *cb; 1124278799Shselasky 1125278799Shselasky cb = udl_cmd_buf_alloc(sc, flags); 1126278799Shselasky if (cb == NULL) 1127278799Shselasky return (EAGAIN); 1128278799Shselasky 1129278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 1130278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 1131278799Shselasky udl_cmd_insert_int_3(cb, off); 1132278799Shselasky udl_cmd_insert_int_1(cb, pixels); 1133278799Shselasky udl_cmd_insert_buf_le16(cb, buf, 2 * pixels); 1134278799Shselasky udl_cmd_buf_send(sc, cb); 1135278799Shselasky 1136278799Shselasky return (0); 1137278799Shselasky} 1138278799Shselasky 1139278799Shselaskystatic int 1140278799Shselaskyudl_cmd_buf_copy_le16(struct udl_softc *sc, uint32_t src, uint32_t dst, 1141278799Shselasky uint8_t pixels, int flags) 1142278799Shselasky{ 1143278799Shselasky struct udl_cmd_buf *cb; 1144278799Shselasky 1145278799Shselasky cb = udl_cmd_buf_alloc(sc, flags); 1146278799Shselasky if (cb == NULL) 1147278799Shselasky return (EAGAIN); 1148278799Shselasky 1149278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 1150278799Shselasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD); 1151278824Shselasky udl_cmd_insert_int_3(cb, dst); 1152278799Shselasky udl_cmd_insert_int_1(cb, pixels); 1153278824Shselasky udl_cmd_insert_int_3(cb, src); 1154278799Shselasky udl_cmd_buf_send(sc, cb); 1155278799Shselasky 1156278799Shselasky return (0); 1157278799Shselasky} 1158