10867995cSHans Petter Selasky /* $OpenBSD: udl.c,v 1.81 2014/12/09 07:05:06 doug Exp $ */ 20867995cSHans Petter Selasky 30867995cSHans Petter Selasky /*- 40867995cSHans Petter Selasky * Copyright (c) 2015 Hans Petter Selasky <hselasky@freebsd.org> 50867995cSHans Petter Selasky * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org> 60867995cSHans Petter Selasky * 70867995cSHans Petter Selasky * Permission to use, copy, modify, and distribute this software for any 80867995cSHans Petter Selasky * purpose with or without fee is hereby granted, provided that the above 90867995cSHans Petter Selasky * copyright notice and this permission notice appear in all copies. 100867995cSHans Petter Selasky * 110867995cSHans Petter Selasky * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 120867995cSHans Petter Selasky * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 130867995cSHans Petter Selasky * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 140867995cSHans Petter Selasky * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 150867995cSHans Petter Selasky * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 160867995cSHans Petter Selasky * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 170867995cSHans Petter Selasky * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 180867995cSHans Petter Selasky */ 190867995cSHans Petter Selasky 200867995cSHans Petter Selasky /* 210867995cSHans Petter Selasky * Driver for the "DisplayLink DL-120 / DL-160" graphic chips based on 220867995cSHans Petter Selasky * the reversed engineered specifications of Florian Echtler 230867995cSHans Petter Selasky * <floe@butterbrot.org>: 240867995cSHans Petter Selasky * 250867995cSHans Petter Selasky * http://floe.butterbrot.org/displaylink/doku.php 260867995cSHans Petter Selasky */ 270867995cSHans Petter Selasky 280867995cSHans Petter Selasky #include <sys/param.h> 290867995cSHans Petter Selasky #include <sys/bus.h> 300867995cSHans Petter Selasky #include <sys/callout.h> 310867995cSHans Petter Selasky #include <sys/conf.h> 320867995cSHans Petter Selasky #include <sys/kernel.h> 330867995cSHans Petter Selasky #include <sys/lock.h> 340867995cSHans Petter Selasky #include <sys/module.h> 350867995cSHans Petter Selasky #include <sys/mutex.h> 360867995cSHans Petter Selasky #include <sys/condvar.h> 370867995cSHans Petter Selasky #include <sys/sysctl.h> 380867995cSHans Petter Selasky #include <sys/systm.h> 390867995cSHans Petter Selasky #include <sys/consio.h> 400867995cSHans Petter Selasky #include <sys/fbio.h> 410867995cSHans Petter Selasky 420867995cSHans Petter Selasky #include <dev/fb/fbreg.h> 430867995cSHans Petter Selasky #include <dev/syscons/syscons.h> 440867995cSHans Petter Selasky 450867995cSHans Petter Selasky #include <dev/videomode/videomode.h> 460867995cSHans Petter Selasky #include <dev/videomode/edidvar.h> 470867995cSHans Petter Selasky 480867995cSHans Petter Selasky #include <dev/usb/usb.h> 490867995cSHans Petter Selasky #include <dev/usb/usbdi.h> 500867995cSHans Petter Selasky #include <dev/usb/usbdi_util.h> 510867995cSHans Petter Selasky #include "usbdevs.h" 520867995cSHans Petter Selasky 530867995cSHans Petter Selasky #include <dev/usb/video/udl.h> 540867995cSHans Petter Selasky 550867995cSHans Petter Selasky #include "fb_if.h" 560867995cSHans Petter Selasky 570867995cSHans Petter Selasky #undef DPRINTF 580867995cSHans Petter Selasky #undef DPRINTFN 590867995cSHans Petter Selasky #define USB_DEBUG_VAR udl_debug 600867995cSHans Petter Selasky #include <dev/usb/usb_debug.h> 610867995cSHans Petter Selasky 62f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, udl, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 63f8d2b1f3SPawel Biernacki "USB UDL"); 6461948b25SHans Petter Selasky 650867995cSHans Petter Selasky #ifdef USB_DEBUG 660867995cSHans Petter Selasky static int udl_debug = 0; 670867995cSHans Petter Selasky 680867995cSHans Petter Selasky SYSCTL_INT(_hw_usb_udl, OID_AUTO, debug, CTLFLAG_RWTUN, 690867995cSHans Petter Selasky &udl_debug, 0, "Debug level"); 700867995cSHans Petter Selasky #endif 710867995cSHans Petter Selasky 7261948b25SHans Petter Selasky #define UDL_FPS_MAX 60 7361948b25SHans Petter Selasky #define UDL_FPS_MIN 1 7461948b25SHans Petter Selasky 7561948b25SHans Petter Selasky static int udl_fps = 25; 7661948b25SHans Petter Selasky SYSCTL_INT(_hw_usb_udl, OID_AUTO, fps, CTLFLAG_RWTUN, 7761948b25SHans Petter Selasky &udl_fps, 0, "Frames Per Second, 1-60"); 7861948b25SHans Petter Selasky 7955e11a03SHans Petter Selasky static struct mtx udl_buffer_mtx; 8055e11a03SHans Petter Selasky static struct udl_buffer_head udl_buffer_head; 8155e11a03SHans Petter Selasky 8255e11a03SHans Petter Selasky MALLOC_DEFINE(M_USB_DL, "USB", "USB DisplayLink"); 8355e11a03SHans Petter Selasky 840867995cSHans Petter Selasky /* 850867995cSHans Petter Selasky * Prototypes. 860867995cSHans Petter Selasky */ 870867995cSHans Petter Selasky static usb_callback_t udl_bulk_write_callback; 880867995cSHans Petter Selasky 890867995cSHans Petter Selasky static device_probe_t udl_probe; 900867995cSHans Petter Selasky static device_attach_t udl_attach; 910867995cSHans Petter Selasky static device_detach_t udl_detach; 920867995cSHans Petter Selasky static fb_getinfo_t udl_fb_getinfo; 9300ad411aSHans Petter Selasky static fb_setblankmode_t udl_fb_setblankmode; 940867995cSHans Petter Selasky 950867995cSHans Petter Selasky static void udl_select_chip(struct udl_softc *, struct usb_attach_arg *); 960867995cSHans Petter Selasky static int udl_init_chip(struct udl_softc *); 970867995cSHans Petter Selasky static void udl_select_mode(struct udl_softc *); 980867995cSHans Petter Selasky static int udl_init_resolution(struct udl_softc *); 990867995cSHans Petter Selasky static void udl_fbmem_alloc(struct udl_softc *); 1000867995cSHans Petter Selasky static int udl_cmd_write_buf_le16(struct udl_softc *, const uint8_t *, uint32_t, uint8_t, int); 1010867995cSHans Petter Selasky static int udl_cmd_buf_copy_le16(struct udl_softc *, uint32_t, uint32_t, uint8_t, int); 1020867995cSHans Petter Selasky static void udl_cmd_insert_int_1(struct udl_cmd_buf *, uint8_t); 1030867995cSHans Petter Selasky static void udl_cmd_insert_int_3(struct udl_cmd_buf *, uint32_t); 1040867995cSHans Petter Selasky static void udl_cmd_insert_buf_le16(struct udl_cmd_buf *, const uint8_t *, uint32_t); 1050867995cSHans Petter Selasky static void udl_cmd_write_reg_1(struct udl_cmd_buf *, uint8_t, uint8_t); 1060867995cSHans Petter Selasky static void udl_cmd_write_reg_3(struct udl_cmd_buf *, uint8_t, uint32_t); 1070867995cSHans Petter Selasky static int udl_power_save(struct udl_softc *, int, int); 1080867995cSHans Petter Selasky 1090867995cSHans Petter Selasky static const struct usb_config udl_config[UDL_N_TRANSFER] = { 1100867995cSHans Petter Selasky [UDL_BULK_WRITE_0] = { 1110867995cSHans Petter Selasky .type = UE_BULK, 1120867995cSHans Petter Selasky .endpoint = UE_ADDR_ANY, 1130867995cSHans Petter Selasky .direction = UE_DIR_TX, 1140867995cSHans Petter Selasky .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, 1150867995cSHans Petter Selasky .bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES, 1160867995cSHans Petter Selasky .callback = &udl_bulk_write_callback, 1170867995cSHans Petter Selasky .frames = UDL_CMD_MAX_FRAMES, 1180867995cSHans Petter Selasky .timeout = 5000, /* 5 seconds */ 1190867995cSHans Petter Selasky }, 1200867995cSHans Petter Selasky [UDL_BULK_WRITE_1] = { 1210867995cSHans Petter Selasky .type = UE_BULK, 1220867995cSHans Petter Selasky .endpoint = UE_ADDR_ANY, 1230867995cSHans Petter Selasky .direction = UE_DIR_TX, 1240867995cSHans Petter Selasky .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, 1250867995cSHans Petter Selasky .bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES, 1260867995cSHans Petter Selasky .callback = &udl_bulk_write_callback, 1270867995cSHans Petter Selasky .frames = UDL_CMD_MAX_FRAMES, 1280867995cSHans Petter Selasky .timeout = 5000, /* 5 seconds */ 1290867995cSHans Petter Selasky }, 1300867995cSHans Petter Selasky }; 1310867995cSHans Petter Selasky 1320867995cSHans Petter Selasky /* 1330867995cSHans Petter Selasky * Driver glue. 1340867995cSHans Petter Selasky */ 1350867995cSHans Petter Selasky static device_method_t udl_methods[] = { 1360867995cSHans Petter Selasky DEVMETHOD(device_probe, udl_probe), 1370867995cSHans Petter Selasky DEVMETHOD(device_attach, udl_attach), 1380867995cSHans Petter Selasky DEVMETHOD(device_detach, udl_detach), 1390867995cSHans Petter Selasky DEVMETHOD(fb_getinfo, udl_fb_getinfo), 1400867995cSHans Petter Selasky DEVMETHOD_END 1410867995cSHans Petter Selasky }; 1420867995cSHans Petter Selasky 1430867995cSHans Petter Selasky static driver_t udl_driver = { 1440867995cSHans Petter Selasky .name = "udl", 1450867995cSHans Petter Selasky .methods = udl_methods, 1460867995cSHans Petter Selasky .size = sizeof(struct udl_softc), 1470867995cSHans Petter Selasky }; 1480867995cSHans Petter Selasky 149bc9372d7SJohn Baldwin DRIVER_MODULE(udl, uhub, udl_driver, NULL, NULL); 1500867995cSHans Petter Selasky MODULE_DEPEND(udl, usb, 1, 1, 1); 1510867995cSHans Petter Selasky MODULE_DEPEND(udl, fbd, 1, 1, 1); 1520867995cSHans Petter Selasky MODULE_DEPEND(udl, videomode, 1, 1, 1); 1530867995cSHans Petter Selasky MODULE_VERSION(udl, 1); 1540867995cSHans Petter Selasky 1550867995cSHans Petter Selasky /* 1560867995cSHans Petter Selasky * Matching devices. 1570867995cSHans Petter Selasky */ 1580867995cSHans Petter Selasky static const STRUCT_USB_HOST_ID udl_devs[] = { 1590867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U, DL120)}, 1600867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U, DL120)}, 1610867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020, DL160)}, 1620867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220, DL165)}, 1630867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60, DL160)}, 1640867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI, DL160)}, 1650867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10, DL120)}, 1660867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI, DLUNK)}, 1670867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008, DL160)}, 1680867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK, DL160)}, 1690867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571, DL160)}, 1700867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061, DL195)}, 1710867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK, DL165)}, 1720867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI, DLUNK)}, 1730867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0, DL120)}, 1740867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV, DL160)}, 175a63352afSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE, DL160)}, 1760867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70, DL125)}, 1770867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2, DLUNK)}, 1784d363380SHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)}, 1794d363380SHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_ITEC, DL165)}, 180c4e11f22SHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DVI_19, DL165)}, 1810867995cSHans Petter Selasky }; 1820867995cSHans Petter Selasky 18355e11a03SHans Petter Selasky static void 18455e11a03SHans Petter Selasky udl_buffer_init(void *arg) 18555e11a03SHans Petter Selasky { 18655e11a03SHans Petter Selasky mtx_init(&udl_buffer_mtx, "USB", "UDL", MTX_DEF); 18755e11a03SHans Petter Selasky TAILQ_INIT(&udl_buffer_head); 18855e11a03SHans Petter Selasky } 18955e11a03SHans Petter Selasky SYSINIT(udl_buffer_init, SI_SUB_LOCK, SI_ORDER_FIRST, udl_buffer_init, NULL); 19055e11a03SHans Petter Selasky 19155e11a03SHans Petter Selasky CTASSERT(sizeof(struct udl_buffer) < PAGE_SIZE); 19255e11a03SHans Petter Selasky 19355e11a03SHans Petter Selasky static void * 19455e11a03SHans Petter Selasky udl_buffer_alloc(uint32_t size) 19555e11a03SHans Petter Selasky { 19655e11a03SHans Petter Selasky struct udl_buffer *buf; 19755e11a03SHans Petter Selasky mtx_lock(&udl_buffer_mtx); 19855e11a03SHans Petter Selasky TAILQ_FOREACH(buf, &udl_buffer_head, entry) { 19955e11a03SHans Petter Selasky if (buf->size == size) { 20055e11a03SHans Petter Selasky TAILQ_REMOVE(&udl_buffer_head, buf, entry); 20155e11a03SHans Petter Selasky break; 20255e11a03SHans Petter Selasky } 20355e11a03SHans Petter Selasky } 20455e11a03SHans Petter Selasky mtx_unlock(&udl_buffer_mtx); 20555e11a03SHans Petter Selasky if (buf != NULL) { 206a998d103SHans Petter Selasky uint8_t *ptr = ((uint8_t *)buf) - size; 20755e11a03SHans Petter Selasky /* wipe and recycle buffer */ 208a998d103SHans Petter Selasky memset(ptr, 0, size); 209a998d103SHans Petter Selasky /* return buffer pointer */ 210a998d103SHans Petter Selasky return (ptr); 21155e11a03SHans Petter Selasky } 21255e11a03SHans Petter Selasky /* allocate new buffer */ 213a998d103SHans Petter Selasky return (malloc(size + sizeof(*buf), M_USB_DL, M_WAITOK | M_ZERO)); 21455e11a03SHans Petter Selasky } 21555e11a03SHans Petter Selasky 21655e11a03SHans Petter Selasky static void 21755e11a03SHans Petter Selasky udl_buffer_free(void *_buf, uint32_t size) 21855e11a03SHans Petter Selasky { 21955e11a03SHans Petter Selasky struct udl_buffer *buf; 22055e11a03SHans Petter Selasky 221a998d103SHans Petter Selasky /* check for NULL pointer */ 222a998d103SHans Petter Selasky if (_buf == NULL) 22355e11a03SHans Petter Selasky return; 224a998d103SHans Petter Selasky /* compute pointer to recycle list */ 225a998d103SHans Petter Selasky buf = (struct udl_buffer *)(((uint8_t *)_buf) + size); 22655e11a03SHans Petter Selasky 22755e11a03SHans Petter Selasky /* 22855e11a03SHans Petter Selasky * Memory mapped buffers should never be freed. 22955e11a03SHans Petter Selasky * Put display buffer into a recycle list. 23055e11a03SHans Petter Selasky */ 23155e11a03SHans Petter Selasky mtx_lock(&udl_buffer_mtx); 23255e11a03SHans Petter Selasky buf->size = size; 23355e11a03SHans Petter Selasky TAILQ_INSERT_TAIL(&udl_buffer_head, buf, entry); 23455e11a03SHans Petter Selasky mtx_unlock(&udl_buffer_mtx); 23555e11a03SHans Petter Selasky } 23655e11a03SHans Petter Selasky 2370867995cSHans Petter Selasky static uint32_t 2380867995cSHans Petter Selasky udl_get_fb_size(struct udl_softc *sc) 2390867995cSHans Petter Selasky { 2400867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2410867995cSHans Petter Selasky 2420867995cSHans Petter Selasky return ((uint32_t)udl_modes[i].hdisplay * 2430867995cSHans Petter Selasky (uint32_t)udl_modes[i].vdisplay * 2); 2440867995cSHans Petter Selasky } 2450867995cSHans Petter Selasky 2460867995cSHans Petter Selasky static uint32_t 2470867995cSHans Petter Selasky udl_get_fb_width(struct udl_softc *sc) 2480867995cSHans Petter Selasky { 2490867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2500867995cSHans Petter Selasky 2514464c5e5SHans Petter Selasky return (udl_modes[i].hdisplay); 2520867995cSHans Petter Selasky } 2530867995cSHans Petter Selasky 2540867995cSHans Petter Selasky static uint32_t 2550867995cSHans Petter Selasky udl_get_fb_height(struct udl_softc *sc) 2560867995cSHans Petter Selasky { 2570867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2580867995cSHans Petter Selasky 2594464c5e5SHans Petter Selasky return (udl_modes[i].vdisplay); 2600867995cSHans Petter Selasky } 2610867995cSHans Petter Selasky 2620867995cSHans Petter Selasky static uint32_t 2630867995cSHans Petter Selasky udl_get_fb_hz(struct udl_softc *sc) 2640867995cSHans Petter Selasky { 2650867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2660867995cSHans Petter Selasky 2670867995cSHans Petter Selasky return (udl_modes[i].hz); 2680867995cSHans Petter Selasky } 2690867995cSHans Petter Selasky 2700867995cSHans Petter Selasky static void 2710867995cSHans Petter Selasky udl_callout(void *arg) 2720867995cSHans Petter Selasky { 2730867995cSHans Petter Selasky struct udl_softc *sc = arg; 2740867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 27561948b25SHans Petter Selasky int fps; 2760867995cSHans Petter Selasky 2770867995cSHans Petter Selasky if (sc->sc_power_save == 0) { 27861948b25SHans Petter Selasky fps = udl_fps; 27961948b25SHans Petter Selasky 28061948b25SHans Petter Selasky /* figure out number of frames per second */ 28161948b25SHans Petter Selasky if (fps < UDL_FPS_MIN) 28261948b25SHans Petter Selasky fps = UDL_FPS_MIN; 28361948b25SHans Petter Selasky else if (fps > UDL_FPS_MAX) 28461948b25SHans Petter Selasky fps = UDL_FPS_MAX; 28561948b25SHans Petter Selasky 2860867995cSHans Petter Selasky if (sc->sc_sync_off >= max) 2870867995cSHans Petter Selasky sc->sc_sync_off = 0; 2880867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 2890867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 29061948b25SHans Petter Selasky } else { 29161948b25SHans Petter Selasky fps = 1; 2920867995cSHans Petter Selasky } 29361948b25SHans Petter Selasky callout_reset(&sc->sc_callout, hz / fps, &udl_callout, sc); 2940867995cSHans Petter Selasky } 2950867995cSHans Petter Selasky 2960867995cSHans Petter Selasky static int 2970867995cSHans Petter Selasky udl_probe(device_t dev) 2980867995cSHans Petter Selasky { 2990867995cSHans Petter Selasky struct usb_attach_arg *uaa = device_get_ivars(dev); 3000867995cSHans Petter Selasky 3010867995cSHans Petter Selasky if (uaa->usb_mode != USB_MODE_HOST) 3020867995cSHans Petter Selasky return (ENXIO); 3030867995cSHans Petter Selasky if (uaa->info.bConfigIndex != 0) 3040867995cSHans Petter Selasky return (ENXIO); 3050867995cSHans Petter Selasky if (uaa->info.bIfaceIndex != 0) 3060867995cSHans Petter Selasky return (ENXIO); 3070867995cSHans Petter Selasky 3080867995cSHans Petter Selasky return (usbd_lookup_id_by_uaa(udl_devs, sizeof(udl_devs), uaa)); 3090867995cSHans Petter Selasky } 3100867995cSHans Petter Selasky 3110867995cSHans Petter Selasky static int 3120867995cSHans Petter Selasky udl_attach(device_t dev) 3130867995cSHans Petter Selasky { 3140867995cSHans Petter Selasky struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 3150867995cSHans Petter Selasky struct sysctl_oid *tree = device_get_sysctl_tree(dev); 3160867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 3170867995cSHans Petter Selasky struct usb_attach_arg *uaa = device_get_ivars(dev); 3180867995cSHans Petter Selasky int error; 3190867995cSHans Petter Selasky int i; 3200867995cSHans Petter Selasky 3210867995cSHans Petter Selasky device_set_usb_desc(dev); 3220867995cSHans Petter Selasky 3230867995cSHans Petter Selasky mtx_init(&sc->sc_mtx, "UDL lock", NULL, MTX_DEF); 3240867995cSHans Petter Selasky cv_init(&sc->sc_cv, "UDLCV"); 3250867995cSHans Petter Selasky callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 3260867995cSHans Petter Selasky sc->sc_udev = uaa->device; 3270867995cSHans Petter Selasky 3280867995cSHans Petter Selasky error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 3290867995cSHans Petter Selasky sc->sc_xfer, udl_config, UDL_N_TRANSFER, sc, &sc->sc_mtx); 3300867995cSHans Petter Selasky 3310867995cSHans Petter Selasky if (error) { 3320867995cSHans Petter Selasky DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error)); 3330867995cSHans Petter Selasky goto detach; 3340867995cSHans Petter Selasky } 3350867995cSHans Petter Selasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_0], &sc->sc_xfer_head[0]); 3360867995cSHans Petter Selasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_1], &sc->sc_xfer_head[1]); 3370867995cSHans Petter Selasky 3380867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_xfer_head[0]); 3390867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_xfer_head[1]); 3400867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_cmd_buf_free); 3410867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_cmd_buf_pending); 3420867995cSHans Petter Selasky 3430867995cSHans Petter Selasky sc->sc_def_chip = -1; 3440867995cSHans Petter Selasky sc->sc_chip = USB_GET_DRIVER_INFO(uaa); 3450867995cSHans Petter Selasky sc->sc_def_mode = -1; 3460867995cSHans Petter Selasky sc->sc_cur_mode = UDL_MAX_MODES; 3470867995cSHans Petter Selasky 3480867995cSHans Petter Selasky /* Allow chip ID to be overwritten */ 3490867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid_force", 3500867995cSHans Petter Selasky CTLFLAG_RWTUN, &sc->sc_def_chip, 0, "chip ID"); 3510867995cSHans Petter Selasky 3520867995cSHans Petter Selasky /* Export current chip ID */ 3530867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid", 3540867995cSHans Petter Selasky CTLFLAG_RD, &sc->sc_chip, 0, "chip ID"); 3550867995cSHans Petter Selasky 3560867995cSHans Petter Selasky if (sc->sc_def_chip > -1 && sc->sc_def_chip <= DLMAX) { 3570867995cSHans Petter Selasky device_printf(dev, "Forcing chip ID to 0x%04x\n", sc->sc_def_chip); 3580867995cSHans Petter Selasky sc->sc_chip = sc->sc_def_chip; 3590867995cSHans Petter Selasky } 3600867995cSHans Petter Selasky /* 3610867995cSHans Petter Selasky * The product might have more than one chip 3620867995cSHans Petter Selasky */ 3630867995cSHans Petter Selasky if (sc->sc_chip == DLUNK) 3640867995cSHans Petter Selasky udl_select_chip(sc, uaa); 3650867995cSHans Petter Selasky 3660867995cSHans Petter Selasky for (i = 0; i != UDL_CMD_MAX_BUFFERS; i++) { 3670867995cSHans Petter Selasky struct udl_cmd_buf *cb = &sc->sc_cmd_buf_temp[i]; 3680867995cSHans Petter Selasky 3690867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 3700867995cSHans Petter Selasky } 3710867995cSHans Petter Selasky 3720867995cSHans Petter Selasky /* 3730867995cSHans Petter Selasky * Initialize chip. 3740867995cSHans Petter Selasky */ 3750867995cSHans Petter Selasky error = udl_init_chip(sc); 3760867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 3770867995cSHans Petter Selasky goto detach; 3780867995cSHans Petter Selasky 3790867995cSHans Petter Selasky /* 3800867995cSHans Petter Selasky * Select edid mode. 3810867995cSHans Petter Selasky */ 3820867995cSHans Petter Selasky udl_select_mode(sc); 3830867995cSHans Petter Selasky 3840867995cSHans Petter Selasky /* Allow default mode to be overwritten */ 3850867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode_force", 3860867995cSHans Petter Selasky CTLFLAG_RWTUN, &sc->sc_def_mode, 0, "mode"); 3870867995cSHans Petter Selasky 3880867995cSHans Petter Selasky /* Export current mode */ 3890867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode", 3900867995cSHans Petter Selasky CTLFLAG_RD, &sc->sc_cur_mode, 0, "mode"); 3910867995cSHans Petter Selasky 3920867995cSHans Petter Selasky i = sc->sc_def_mode; 3930867995cSHans Petter Selasky if (i > -1 && i < UDL_MAX_MODES) { 3940867995cSHans Petter Selasky if (udl_modes[i].chip <= sc->sc_chip) { 3950867995cSHans Petter Selasky device_printf(dev, "Forcing mode to %d\n", i); 3960867995cSHans Petter Selasky sc->sc_cur_mode = i; 3970867995cSHans Petter Selasky } 3980867995cSHans Petter Selasky } 3990867995cSHans Petter Selasky /* Printout current mode */ 4000867995cSHans Petter Selasky device_printf(dev, "Mode selected %dx%d @ %dHz\n", 4010867995cSHans Petter Selasky (int)udl_get_fb_width(sc), 4020867995cSHans Petter Selasky (int)udl_get_fb_height(sc), 4030867995cSHans Petter Selasky (int)udl_get_fb_hz(sc)); 4040867995cSHans Petter Selasky 4050867995cSHans Petter Selasky udl_init_resolution(sc); 4060867995cSHans Petter Selasky 4070867995cSHans Petter Selasky /* Allocate frame buffer */ 4080867995cSHans Petter Selasky udl_fbmem_alloc(sc); 4090867995cSHans Petter Selasky 4100867995cSHans Petter Selasky UDL_LOCK(sc); 4110867995cSHans Petter Selasky udl_callout(sc); 4120867995cSHans Petter Selasky UDL_UNLOCK(sc); 4130867995cSHans Petter Selasky 4140867995cSHans Petter Selasky sc->sc_fb_info.fb_name = device_get_nameunit(dev); 4150867995cSHans Petter Selasky sc->sc_fb_info.fb_size = sc->sc_fb_size; 4160867995cSHans Petter Selasky sc->sc_fb_info.fb_bpp = 16; 4170867995cSHans Petter Selasky sc->sc_fb_info.fb_depth = 16; 4180867995cSHans Petter Selasky sc->sc_fb_info.fb_width = udl_get_fb_width(sc); 4190867995cSHans Petter Selasky sc->sc_fb_info.fb_height = udl_get_fb_height(sc); 4200867995cSHans Petter Selasky sc->sc_fb_info.fb_stride = sc->sc_fb_info.fb_width * 2; 421b161d698SHans Petter Selasky sc->sc_fb_info.fb_pbase = 0; 4220867995cSHans Petter Selasky sc->sc_fb_info.fb_vbase = (uintptr_t)sc->sc_fb_addr; 42300ad411aSHans Petter Selasky sc->sc_fb_info.fb_priv = sc; 42400ad411aSHans Petter Selasky sc->sc_fb_info.setblankmode = &udl_fb_setblankmode; 4250867995cSHans Petter Selasky 426*5b56413dSWarner Losh sc->sc_fbdev = device_add_child(dev, "fbd", DEVICE_UNIT_ANY); 4270867995cSHans Petter Selasky if (sc->sc_fbdev == NULL) 4280867995cSHans Petter Selasky goto detach; 4290867995cSHans Petter Selasky if (device_probe_and_attach(sc->sc_fbdev) != 0) 4300867995cSHans Petter Selasky goto detach; 4310867995cSHans Petter Selasky 4320867995cSHans Petter Selasky return (0); 4330867995cSHans Petter Selasky 4340867995cSHans Petter Selasky detach: 4350867995cSHans Petter Selasky udl_detach(dev); 4360867995cSHans Petter Selasky 4370867995cSHans Petter Selasky return (ENXIO); 4380867995cSHans Petter Selasky } 4390867995cSHans Petter Selasky 4400867995cSHans Petter Selasky static int 4410867995cSHans Petter Selasky udl_detach(device_t dev) 4420867995cSHans Petter Selasky { 4430867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 4440867995cSHans Petter Selasky 445d3bf5efcSHans Petter Selasky /* delete all child devices */ 446d3bf5efcSHans Petter Selasky device_delete_children(dev); 4470867995cSHans Petter Selasky 4480867995cSHans Petter Selasky UDL_LOCK(sc); 4490867995cSHans Petter Selasky sc->sc_gone = 1; 4500867995cSHans Petter Selasky callout_stop(&sc->sc_callout); 4510867995cSHans Petter Selasky UDL_UNLOCK(sc); 4520867995cSHans Petter Selasky 4530867995cSHans Petter Selasky usbd_transfer_unsetup(sc->sc_xfer, UDL_N_TRANSFER); 4540867995cSHans Petter Selasky 4550867995cSHans Petter Selasky callout_drain(&sc->sc_callout); 4560867995cSHans Petter Selasky 4570867995cSHans Petter Selasky mtx_destroy(&sc->sc_mtx); 4580867995cSHans Petter Selasky cv_destroy(&sc->sc_cv); 4590867995cSHans Petter Selasky 46055e11a03SHans Petter Selasky /* put main framebuffer into a recycle list, if any */ 46155e11a03SHans Petter Selasky udl_buffer_free(sc->sc_fb_addr, sc->sc_fb_size); 46255e11a03SHans Petter Selasky 46355e11a03SHans Petter Selasky /* free shadow framebuffer memory, if any */ 46455e11a03SHans Petter Selasky free(sc->sc_fb_copy, M_USB_DL); 4650867995cSHans Petter Selasky 4660867995cSHans Petter Selasky return (0); 4670867995cSHans Petter Selasky } 4680867995cSHans Petter Selasky 4690867995cSHans Petter Selasky static struct fb_info * 4700867995cSHans Petter Selasky udl_fb_getinfo(device_t dev) 4710867995cSHans Petter Selasky { 4720867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 4730867995cSHans Petter Selasky 4740867995cSHans Petter Selasky return (&sc->sc_fb_info); 4750867995cSHans Petter Selasky } 4760867995cSHans Petter Selasky 4770867995cSHans Petter Selasky static int 47800ad411aSHans Petter Selasky udl_fb_setblankmode(void *arg, int mode) 4790867995cSHans Petter Selasky { 48000ad411aSHans Petter Selasky struct udl_softc *sc = arg; 4810867995cSHans Petter Selasky 4820867995cSHans Petter Selasky switch (mode) { 4830867995cSHans Petter Selasky case V_DISPLAY_ON: 4840867995cSHans Petter Selasky udl_power_save(sc, 1, M_WAITOK); 4850867995cSHans Petter Selasky break; 4860867995cSHans Petter Selasky case V_DISPLAY_BLANK: 4870867995cSHans Petter Selasky udl_power_save(sc, 1, M_WAITOK); 4880867995cSHans Petter Selasky if (sc->sc_fb_addr != 0) { 4890867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 4900867995cSHans Petter Selasky 4910867995cSHans Petter Selasky memset((void *)sc->sc_fb_addr, 0, max); 4920867995cSHans Petter Selasky } 4930867995cSHans Petter Selasky break; 4940867995cSHans Petter Selasky case V_DISPLAY_STAND_BY: 4950867995cSHans Petter Selasky case V_DISPLAY_SUSPEND: 4960867995cSHans Petter Selasky udl_power_save(sc, 0, M_WAITOK); 4970867995cSHans Petter Selasky break; 4980867995cSHans Petter Selasky } 4990867995cSHans Petter Selasky return (0); 5000867995cSHans Petter Selasky } 5010867995cSHans Petter Selasky 5020867995cSHans Petter Selasky static struct udl_cmd_buf * 503b41892e1SHans Petter Selasky udl_cmd_buf_alloc_locked(struct udl_softc *sc, int flags) 5040867995cSHans Petter Selasky { 5050867995cSHans Petter Selasky struct udl_cmd_buf *cb; 5060867995cSHans Petter Selasky 5070867995cSHans Petter Selasky while ((cb = TAILQ_FIRST(&sc->sc_cmd_buf_free)) == NULL) { 5080867995cSHans Petter Selasky if (flags != M_WAITOK) 5090867995cSHans Petter Selasky break; 5100867995cSHans Petter Selasky cv_wait(&sc->sc_cv, &sc->sc_mtx); 5110867995cSHans Petter Selasky } 5120867995cSHans Petter Selasky if (cb != NULL) { 5130867995cSHans Petter Selasky TAILQ_REMOVE(&sc->sc_cmd_buf_free, cb, entry); 5140867995cSHans Petter Selasky cb->off = 0; 5150867995cSHans Petter Selasky } 516b41892e1SHans Petter Selasky return (cb); 517b41892e1SHans Petter Selasky } 518b41892e1SHans Petter Selasky 519b41892e1SHans Petter Selasky static struct udl_cmd_buf * 520b41892e1SHans Petter Selasky udl_cmd_buf_alloc(struct udl_softc *sc, int flags) 521b41892e1SHans Petter Selasky { 522b41892e1SHans Petter Selasky struct udl_cmd_buf *cb; 523b41892e1SHans Petter Selasky 524b41892e1SHans Petter Selasky UDL_LOCK(sc); 525b41892e1SHans Petter Selasky cb = udl_cmd_buf_alloc_locked(sc, flags); 5260867995cSHans Petter Selasky UDL_UNLOCK(sc); 5270867995cSHans Petter Selasky return (cb); 5280867995cSHans Petter Selasky } 5290867995cSHans Petter Selasky 5300867995cSHans Petter Selasky static void 5310867995cSHans Petter Selasky udl_cmd_buf_send(struct udl_softc *sc, struct udl_cmd_buf *cb) 5320867995cSHans Petter Selasky { 5330867995cSHans Petter Selasky UDL_LOCK(sc); 5340867995cSHans Petter Selasky if (sc->sc_gone) { 5350867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 5360867995cSHans Petter Selasky } else { 5370867995cSHans Petter Selasky /* mark end of command stack */ 5380867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 5390867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_EOC); 5400867995cSHans Petter Selasky 5410867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_pending, cb, entry); 5420867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 5430867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 5440867995cSHans Petter Selasky } 5450867995cSHans Petter Selasky UDL_UNLOCK(sc); 5460867995cSHans Petter Selasky } 5470867995cSHans Petter Selasky 5480867995cSHans Petter Selasky static struct udl_cmd_buf * 549b41892e1SHans Petter Selasky udl_fb_synchronize_locked(struct udl_softc *sc) 5500867995cSHans Petter Selasky { 5510867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 5520867995cSHans Petter Selasky 553b161d698SHans Petter Selasky /* check if framebuffer is not ready */ 554b161d698SHans Petter Selasky if (sc->sc_fb_addr == NULL || 555b161d698SHans Petter Selasky sc->sc_fb_copy == NULL) 556b161d698SHans Petter Selasky return (NULL); 557b161d698SHans Petter Selasky 5580867995cSHans Petter Selasky while (sc->sc_sync_off < max) { 5590867995cSHans Petter Selasky uint32_t delta = max - sc->sc_sync_off; 5600867995cSHans Petter Selasky 5610867995cSHans Petter Selasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 5620867995cSHans Petter Selasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 5630867995cSHans Petter Selasky if (bcmp(sc->sc_fb_addr + sc->sc_sync_off, sc->sc_fb_copy + sc->sc_sync_off, delta) != 0) { 5640867995cSHans Petter Selasky struct udl_cmd_buf *cb; 5650867995cSHans Petter Selasky 566b41892e1SHans Petter Selasky cb = udl_cmd_buf_alloc_locked(sc, M_NOWAIT); 5670867995cSHans Petter Selasky if (cb == NULL) 5680867995cSHans Petter Selasky goto done; 5690867995cSHans Petter Selasky memcpy(sc->sc_fb_copy + sc->sc_sync_off, 5700867995cSHans Petter Selasky sc->sc_fb_addr + sc->sc_sync_off, delta); 5710867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 5720867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 5730867995cSHans Petter Selasky udl_cmd_insert_int_3(cb, sc->sc_sync_off); 5740867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, delta / 2); 5750867995cSHans Petter Selasky udl_cmd_insert_buf_le16(cb, sc->sc_fb_copy + sc->sc_sync_off, delta); 5760867995cSHans Petter Selasky sc->sc_sync_off += delta; 5770867995cSHans Petter Selasky return (cb); 5780867995cSHans Petter Selasky } else { 5790867995cSHans Petter Selasky sc->sc_sync_off += delta; 5800867995cSHans Petter Selasky } 5810867995cSHans Petter Selasky } 5820867995cSHans Petter Selasky done: 5830867995cSHans Petter Selasky return (NULL); 5840867995cSHans Petter Selasky } 5850867995cSHans Petter Selasky 5860867995cSHans Petter Selasky static void 5870867995cSHans Petter Selasky udl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 5880867995cSHans Petter Selasky { 5890867995cSHans Petter Selasky struct udl_softc *sc = usbd_xfer_softc(xfer); 5900867995cSHans Petter Selasky struct udl_cmd_head *phead = usbd_xfer_get_priv(xfer); 5910867995cSHans Petter Selasky struct udl_cmd_buf *cb; 5920867995cSHans Petter Selasky unsigned i; 5930867995cSHans Petter Selasky 5940867995cSHans Petter Selasky switch (USB_GET_STATE(xfer)) { 5950867995cSHans Petter Selasky case USB_ST_TRANSFERRED: 5960867995cSHans Petter Selasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 5970867995cSHans Petter Selasky case USB_ST_SETUP: 5980867995cSHans Petter Selasky tr_setup: 5990867995cSHans Petter Selasky for (i = 0; i != UDL_CMD_MAX_FRAMES; i++) { 6000867995cSHans Petter Selasky cb = TAILQ_FIRST(&sc->sc_cmd_buf_pending); 6010867995cSHans Petter Selasky if (cb == NULL) { 602b41892e1SHans Petter Selasky cb = udl_fb_synchronize_locked(sc); 6030867995cSHans Petter Selasky if (cb == NULL) 6040867995cSHans Petter Selasky break; 605b161d698SHans Petter Selasky } else { 6060867995cSHans Petter Selasky TAILQ_REMOVE(&sc->sc_cmd_buf_pending, cb, entry); 607b161d698SHans Petter Selasky } 6080867995cSHans Petter Selasky TAILQ_INSERT_TAIL(phead, cb, entry); 6090867995cSHans Petter Selasky usbd_xfer_set_frame_data(xfer, i, cb->buf, cb->off); 6100867995cSHans Petter Selasky } 6110867995cSHans Petter Selasky if (i != 0) { 6120867995cSHans Petter Selasky usbd_xfer_set_frames(xfer, i); 6130867995cSHans Petter Selasky usbd_transfer_submit(xfer); 6140867995cSHans Petter Selasky } 6150867995cSHans Petter Selasky break; 6160867995cSHans Petter Selasky default: 6170867995cSHans Petter Selasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 6180867995cSHans Petter Selasky if (error != USB_ERR_CANCELLED) { 6190867995cSHans Petter Selasky /* try clear stall first */ 6200867995cSHans Petter Selasky usbd_xfer_set_stall(xfer); 6210867995cSHans Petter Selasky goto tr_setup; 6220867995cSHans Petter Selasky } 6230867995cSHans Petter Selasky break; 6240867995cSHans Petter Selasky } 6250867995cSHans Petter Selasky /* wakeup any waiters */ 6260867995cSHans Petter Selasky cv_signal(&sc->sc_cv); 6270867995cSHans Petter Selasky } 6280867995cSHans Petter Selasky 6290867995cSHans Petter Selasky static int 6300867995cSHans Petter Selasky udl_power_save(struct udl_softc *sc, int on, int flags) 6310867995cSHans Petter Selasky { 6320867995cSHans Petter Selasky struct udl_cmd_buf *cb; 6330867995cSHans Petter Selasky 6340867995cSHans Petter Selasky /* get new buffer */ 6350867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 6360867995cSHans Petter Selasky if (cb == NULL) 6370867995cSHans Petter Selasky return (EAGAIN); 6380867995cSHans Petter Selasky 6390867995cSHans Petter Selasky DPRINTF("screen %s\n", on ? "ON" : "OFF"); 6400867995cSHans Petter Selasky 6410867995cSHans Petter Selasky sc->sc_power_save = on ? 0 : 1; 6420867995cSHans Petter Selasky 6430867995cSHans Petter Selasky if (on) 6440867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 6450867995cSHans Petter Selasky else 6460867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF); 6470867995cSHans Petter Selasky 6480867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 6490867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 6500867995cSHans Petter Selasky return (0); 6510867995cSHans Petter Selasky } 6520867995cSHans Petter Selasky 6530867995cSHans Petter Selasky static int 6540867995cSHans Petter Selasky udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r, 6550867995cSHans Petter Selasky uint16_t index, uint16_t value, uint8_t *buf, size_t len) 6560867995cSHans Petter Selasky { 6570867995cSHans Petter Selasky usb_device_request_t req; 6580867995cSHans Petter Selasky int error; 6590867995cSHans Petter Selasky 6600867995cSHans Petter Selasky req.bmRequestType = rt; 6610867995cSHans Petter Selasky req.bRequest = r; 6620867995cSHans Petter Selasky USETW(req.wIndex, index); 6630867995cSHans Petter Selasky USETW(req.wValue, value); 6640867995cSHans Petter Selasky USETW(req.wLength, len); 6650867995cSHans Petter Selasky 6660867995cSHans Petter Selasky error = usbd_do_request_flags(sc->sc_udev, NULL, 6670867995cSHans Petter Selasky &req, buf, 0, NULL, USB_DEFAULT_TIMEOUT); 6680867995cSHans Petter Selasky 6690867995cSHans Petter Selasky DPRINTF("%s\n", usbd_errstr(error)); 6700867995cSHans Petter Selasky 6710867995cSHans Petter Selasky return (error); 6720867995cSHans Petter Selasky } 6730867995cSHans Petter Selasky 6740867995cSHans Petter Selasky static int 6750867995cSHans Petter Selasky udl_poll(struct udl_softc *sc, uint32_t *buf) 6760867995cSHans Petter Selasky { 6770867995cSHans Petter Selasky uint32_t lbuf; 6780867995cSHans Petter Selasky int error; 6790867995cSHans Petter Selasky 6800867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6810867995cSHans Petter Selasky UDL_CTRL_CMD_POLL, 0x0000, 0x0000, (uint8_t *)&lbuf, sizeof(lbuf)); 6820867995cSHans Petter Selasky if (error == USB_ERR_NORMAL_COMPLETION) 6830867995cSHans Petter Selasky *buf = le32toh(lbuf); 6840867995cSHans Petter Selasky return (error); 6850867995cSHans Petter Selasky } 6860867995cSHans Petter Selasky 6870867995cSHans Petter Selasky static int 6880867995cSHans Petter Selasky udl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf) 6890867995cSHans Petter Selasky { 6900867995cSHans Petter Selasky uint8_t lbuf[1]; 6910867995cSHans Petter Selasky int error; 6920867995cSHans Petter Selasky 6930867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6940867995cSHans Petter Selasky UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1); 6950867995cSHans Petter Selasky if (error == USB_ERR_NORMAL_COMPLETION) 6960867995cSHans Petter Selasky *buf = *(uint8_t *)lbuf; 6970867995cSHans Petter Selasky return (error); 6980867995cSHans Petter Selasky } 6990867995cSHans Petter Selasky 7000867995cSHans Petter Selasky static int 7010867995cSHans Petter Selasky udl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf) 7020867995cSHans Petter Selasky { 7030867995cSHans Petter Selasky int error; 7040867995cSHans Petter Selasky 7050867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 7060867995cSHans Petter Selasky UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1); 7070867995cSHans Petter Selasky return (error); 7080867995cSHans Petter Selasky } 7090867995cSHans Petter Selasky 7100867995cSHans Petter Selasky static int 7110867995cSHans Petter Selasky udl_read_edid(struct udl_softc *sc, uint8_t *buf) 7120867995cSHans Petter Selasky { 7130867995cSHans Petter Selasky uint8_t lbuf[64]; 7140867995cSHans Petter Selasky uint16_t offset; 7150867995cSHans Petter Selasky int error; 7160867995cSHans Petter Selasky 7170867995cSHans Petter Selasky offset = 0; 7180867995cSHans Petter Selasky 7190867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 7200867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 7210867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 7220867995cSHans Petter Selasky goto fail; 7230867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 63); 7240867995cSHans Petter Selasky offset += 63; 7250867995cSHans Petter Selasky 7260867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 7270867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 7280867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 7290867995cSHans Petter Selasky goto fail; 7300867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 63); 7310867995cSHans Petter Selasky offset += 63; 7320867995cSHans Petter Selasky 7330867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 7340867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3); 7350867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 7360867995cSHans Petter Selasky goto fail; 7370867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 2); 7380867995cSHans Petter Selasky fail: 7390867995cSHans Petter Selasky return (error); 7400867995cSHans Petter Selasky } 7410867995cSHans Petter Selasky 7420867995cSHans Petter Selasky static uint8_t 7430867995cSHans Petter Selasky udl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t hz, 7440867995cSHans Petter Selasky uint16_t chip, uint32_t clock) 7450867995cSHans Petter Selasky { 7460867995cSHans Petter Selasky uint8_t idx; 7470867995cSHans Petter Selasky 7480867995cSHans Petter Selasky /* 7490867995cSHans Petter Selasky * Check first if we have a matching mode with pixelclock 7500867995cSHans Petter Selasky */ 7510867995cSHans Petter Selasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 7520867995cSHans Petter Selasky if ((udl_modes[idx].hdisplay == hdisplay) && 7530867995cSHans Petter Selasky (udl_modes[idx].vdisplay == vdisplay) && 7540867995cSHans Petter Selasky (udl_modes[idx].clock == clock) && 7550867995cSHans Petter Selasky (udl_modes[idx].chip <= chip)) { 7560867995cSHans Petter Selasky return (idx); 7570867995cSHans Petter Selasky } 7580867995cSHans Petter Selasky } 7590867995cSHans Petter Selasky 7600867995cSHans Petter Selasky /* 7610867995cSHans Petter Selasky * If not, check for matching mode with update frequency 7620867995cSHans Petter Selasky */ 7630867995cSHans Petter Selasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 7640867995cSHans Petter Selasky if ((udl_modes[idx].hdisplay == hdisplay) && 7650867995cSHans Petter Selasky (udl_modes[idx].vdisplay == vdisplay) && 7660867995cSHans Petter Selasky (udl_modes[idx].hz == hz) && 7670867995cSHans Petter Selasky (udl_modes[idx].chip <= chip)) { 7680867995cSHans Petter Selasky return (idx); 7690867995cSHans Petter Selasky } 7700867995cSHans Petter Selasky } 7710867995cSHans Petter Selasky return (idx); 7720867995cSHans Petter Selasky } 7730867995cSHans Petter Selasky 7740867995cSHans Petter Selasky static void 7750867995cSHans Petter Selasky udl_select_chip(struct udl_softc *sc, struct usb_attach_arg *uaa) 7760867995cSHans Petter Selasky { 7770867995cSHans Petter Selasky const char *pserial; 7780867995cSHans Petter Selasky 7790867995cSHans Petter Selasky pserial = usb_get_serial(uaa->device); 7800867995cSHans Petter Selasky 7810867995cSHans Petter Selasky sc->sc_chip = DL120; 7820867995cSHans Petter Selasky 7830867995cSHans Petter Selasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 7840867995cSHans Petter Selasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_WSDVI)) { 7850867995cSHans Petter Selasky /* 7860867995cSHans Petter Selasky * WS Tech DVI is DL120 or DL160. All deviced uses the 7870867995cSHans Petter Selasky * same revision (0.04) so iSerialNumber must be used 788a924b485SGordon Bergling * to determine which chip it is. 7890867995cSHans Petter Selasky */ 7900867995cSHans Petter Selasky 7910867995cSHans Petter Selasky if (strlen(pserial) > 7) { 7920867995cSHans Petter Selasky if (strncmp(pserial, "0198-13", 7) == 0) 7930867995cSHans Petter Selasky sc->sc_chip = DL160; 7940867995cSHans Petter Selasky } 7950867995cSHans Petter Selasky DPRINTF("iSerialNumber (%s) used to select chip (%d)\n", 7960867995cSHans Petter Selasky pserial, sc->sc_chip); 7970867995cSHans Petter Selasky } 7980867995cSHans Petter Selasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 7990867995cSHans Petter Selasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_SWDVI)) { 8000867995cSHans Petter Selasky /* 8010867995cSHans Petter Selasky * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision 8020867995cSHans Petter Selasky * can be used to differ between DL1x0 and DL1x5. Minor to 8030867995cSHans Petter Selasky * differ between DL1x5. iSerialNumber seems not to be uniqe. 8040867995cSHans Petter Selasky */ 8050867995cSHans Petter Selasky 8060867995cSHans Petter Selasky sc->sc_chip = DL160; 8070867995cSHans Petter Selasky 8080867995cSHans Petter Selasky if (uaa->info.bcdDevice >= 0x100) { 8090867995cSHans Petter Selasky sc->sc_chip = DL165; 8100867995cSHans Petter Selasky if (uaa->info.bcdDevice == 0x104) 8110867995cSHans Petter Selasky sc->sc_chip = DL195; 8120867995cSHans Petter Selasky if (uaa->info.bcdDevice == 0x108) 8130867995cSHans Petter Selasky sc->sc_chip = DL125; 8140867995cSHans Petter Selasky } 8150867995cSHans Petter Selasky DPRINTF("bcdDevice (%02x) used to select chip (%d)\n", 8160867995cSHans Petter Selasky uaa->info.bcdDevice, sc->sc_chip); 8170867995cSHans Petter Selasky } 8180867995cSHans Petter Selasky } 8190867995cSHans Petter Selasky 8200867995cSHans Petter Selasky static int 8210867995cSHans Petter Selasky udl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len) 8220867995cSHans Petter Selasky { 8230867995cSHans Petter Selasky int error; 8240867995cSHans Petter Selasky 8250867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 8260867995cSHans Petter Selasky UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len); 8270867995cSHans Petter Selasky return (error); 8280867995cSHans Petter Selasky } 8290867995cSHans Petter Selasky 8300867995cSHans Petter Selasky static void 8310867995cSHans Petter Selasky udl_fbmem_alloc(struct udl_softc *sc) 8320867995cSHans Petter Selasky { 8330867995cSHans Petter Selasky uint32_t size; 8340867995cSHans Petter Selasky 8350867995cSHans Petter Selasky size = udl_get_fb_size(sc); 8360867995cSHans Petter Selasky size = round_page(size); 83755e11a03SHans Petter Selasky /* check for zero size */ 83855e11a03SHans Petter Selasky if (size == 0) 83955e11a03SHans Petter Selasky size = PAGE_SIZE; 84061948b25SHans Petter Selasky /* 84161948b25SHans Petter Selasky * It is assumed that allocations above PAGE_SIZE bytes will 84261948b25SHans Petter Selasky * be PAGE_SIZE aligned for use with mmap() 84361948b25SHans Petter Selasky */ 84455e11a03SHans Petter Selasky sc->sc_fb_addr = udl_buffer_alloc(size); 84555e11a03SHans Petter Selasky sc->sc_fb_copy = malloc(size, M_USB_DL, M_WAITOK | M_ZERO); 8460867995cSHans Petter Selasky sc->sc_fb_size = size; 8470867995cSHans Petter Selasky } 8480867995cSHans Petter Selasky 8490867995cSHans Petter Selasky static void 8500867995cSHans Petter Selasky udl_cmd_insert_int_1(struct udl_cmd_buf *cb, uint8_t value) 8510867995cSHans Petter Selasky { 8520867995cSHans Petter Selasky 8530867995cSHans Petter Selasky cb->buf[cb->off] = value; 8540867995cSHans Petter Selasky cb->off += 1; 8550867995cSHans Petter Selasky } 8560867995cSHans Petter Selasky 8570867995cSHans Petter Selasky #if 0 8580867995cSHans Petter Selasky static void 8590867995cSHans Petter Selasky udl_cmd_insert_int_2(struct udl_cmd_buf *cb, uint16_t value) 8600867995cSHans Petter Selasky { 8610867995cSHans Petter Selasky uint16_t lvalue; 8620867995cSHans Petter Selasky 8630867995cSHans Petter Selasky lvalue = htobe16(value); 8640867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 2); 8650867995cSHans Petter Selasky 8660867995cSHans Petter Selasky cb->off += 2; 8670867995cSHans Petter Selasky } 8680867995cSHans Petter Selasky 8690867995cSHans Petter Selasky #endif 8700867995cSHans Petter Selasky 8710867995cSHans Petter Selasky static void 8720867995cSHans Petter Selasky udl_cmd_insert_int_3(struct udl_cmd_buf *cb, uint32_t value) 8730867995cSHans Petter Selasky { 8740867995cSHans Petter Selasky uint32_t lvalue; 8750867995cSHans Petter Selasky 8760867995cSHans Petter Selasky #if BYTE_ORDER == BIG_ENDIAN 8770867995cSHans Petter Selasky lvalue = htobe32(value) << 8; 8780867995cSHans Petter Selasky #else 8790867995cSHans Petter Selasky lvalue = htobe32(value) >> 8; 8800867995cSHans Petter Selasky #endif 8810867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 3); 8820867995cSHans Petter Selasky 8830867995cSHans Petter Selasky cb->off += 3; 8840867995cSHans Petter Selasky } 8850867995cSHans Petter Selasky 8860867995cSHans Petter Selasky #if 0 8870867995cSHans Petter Selasky static void 8880867995cSHans Petter Selasky udl_cmd_insert_int_4(struct udl_cmd_buf *cb, uint32_t value) 8890867995cSHans Petter Selasky { 8900867995cSHans Petter Selasky uint32_t lvalue; 8910867995cSHans Petter Selasky 8920867995cSHans Petter Selasky lvalue = htobe32(value); 8930867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 4); 8940867995cSHans Petter Selasky 8950867995cSHans Petter Selasky cb->off += 4; 8960867995cSHans Petter Selasky } 8970867995cSHans Petter Selasky 8980867995cSHans Petter Selasky #endif 8990867995cSHans Petter Selasky 9000867995cSHans Petter Selasky static void 9010867995cSHans Petter Selasky udl_cmd_insert_buf_le16(struct udl_cmd_buf *cb, const uint8_t *buf, uint32_t len) 9020867995cSHans Petter Selasky { 9030867995cSHans Petter Selasky uint32_t x; 9040867995cSHans Petter Selasky 9050867995cSHans Petter Selasky for (x = 0; x != len; x += 2) { 9060867995cSHans Petter Selasky /* byte swap from little endian to big endian */ 9070867995cSHans Petter Selasky cb->buf[cb->off + x + 0] = buf[x + 1]; 9080867995cSHans Petter Selasky cb->buf[cb->off + x + 1] = buf[x + 0]; 9090867995cSHans Petter Selasky } 9100867995cSHans Petter Selasky cb->off += len; 9110867995cSHans Petter Selasky } 9120867995cSHans Petter Selasky 9130867995cSHans Petter Selasky static void 9140867995cSHans Petter Selasky udl_cmd_write_reg_1(struct udl_cmd_buf *cb, uint8_t reg, uint8_t val) 9150867995cSHans Petter Selasky { 9160867995cSHans Petter Selasky 9170867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 9180867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_REG_WRITE_1); 9190867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, reg); 9200867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, val); 9210867995cSHans Petter Selasky } 9220867995cSHans Petter Selasky 9230867995cSHans Petter Selasky static void 9240867995cSHans Petter Selasky udl_cmd_write_reg_3(struct udl_cmd_buf *cb, uint8_t reg, uint32_t val) 9250867995cSHans Petter Selasky { 9260867995cSHans Petter Selasky 9270867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 0, (val >> 16) & 0xff); 9280867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 1, (val >> 8) & 0xff); 9290867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 2, (val >> 0) & 0xff); 9300867995cSHans Petter Selasky } 9310867995cSHans Petter Selasky 9320867995cSHans Petter Selasky static int 9330867995cSHans Petter Selasky udl_init_chip(struct udl_softc *sc) 9340867995cSHans Petter Selasky { 9350867995cSHans Petter Selasky uint32_t ui32; 9360867995cSHans Petter Selasky uint8_t ui8; 9370867995cSHans Petter Selasky int error; 9380867995cSHans Petter Selasky 9390867995cSHans Petter Selasky error = udl_poll(sc, &ui32); 9400867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9410867995cSHans Petter Selasky return (error); 9420867995cSHans Petter Selasky DPRINTF("poll=0x%08x\n", ui32); 9430867995cSHans Petter Selasky 9440867995cSHans Petter Selasky /* Some products may use later chip too */ 9450867995cSHans Petter Selasky switch (ui32 & 0xff) { 9460867995cSHans Petter Selasky case 0xf1: /* DL1x5 */ 9470867995cSHans Petter Selasky switch (sc->sc_chip) { 9480867995cSHans Petter Selasky case DL120: 9490867995cSHans Petter Selasky sc->sc_chip = DL125; 9500867995cSHans Petter Selasky break; 9510867995cSHans Petter Selasky case DL160: 9520867995cSHans Petter Selasky sc->sc_chip = DL165; 9530867995cSHans Petter Selasky break; 9540867995cSHans Petter Selasky } 9550867995cSHans Petter Selasky break; 9560867995cSHans Petter Selasky } 9570867995cSHans Petter Selasky DPRINTF("chip 0x%04x\n", sc->sc_chip); 9580867995cSHans Petter Selasky 9590867995cSHans Petter Selasky error = udl_read_1(sc, 0xc484, &ui8); 9600867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9610867995cSHans Petter Selasky return (error); 9620867995cSHans Petter Selasky DPRINTF("read 0x%02x from 0xc484\n", ui8); 9630867995cSHans Petter Selasky 9640867995cSHans Petter Selasky error = udl_write_1(sc, 0xc41f, 0x01); 9650867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9660867995cSHans Petter Selasky return (error); 9670867995cSHans Petter Selasky DPRINTF("write 0x01 to 0xc41f\n"); 9680867995cSHans Petter Selasky 9690867995cSHans Petter Selasky error = udl_read_edid(sc, sc->sc_edid); 9700867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9710867995cSHans Petter Selasky return (error); 9720867995cSHans Petter Selasky DPRINTF("read EDID\n"); 9730867995cSHans Petter Selasky 9740867995cSHans Petter Selasky error = udl_set_enc_key(sc, __DECONST(void *, udl_null_key_1), 9750867995cSHans Petter Selasky sizeof(udl_null_key_1)); 9760867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9770867995cSHans Petter Selasky return (error); 9780867995cSHans Petter Selasky DPRINTF("set encryption key\n"); 9790867995cSHans Petter Selasky 9800867995cSHans Petter Selasky error = udl_write_1(sc, 0xc40b, 0x00); 9810867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9820867995cSHans Petter Selasky return (error); 9830867995cSHans Petter Selasky DPRINTF("write 0x00 to 0xc40b\n"); 9840867995cSHans Petter Selasky 9850867995cSHans Petter Selasky return (USB_ERR_NORMAL_COMPLETION); 9860867995cSHans Petter Selasky } 9870867995cSHans Petter Selasky 9880867995cSHans Petter Selasky static void 9890867995cSHans Petter Selasky udl_init_fb_offsets(struct udl_cmd_buf *cb, uint32_t start16, uint32_t stride16, 9900867995cSHans Petter Selasky uint32_t start8, uint32_t stride8) 9910867995cSHans Petter Selasky { 9920867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 9930867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START16, start16); 9940867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE16, stride16); 9950867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START8, start8); 9960867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE8, stride8); 9970867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 9980867995cSHans Petter Selasky } 9990867995cSHans Petter Selasky 10000867995cSHans Petter Selasky static int 10010867995cSHans Petter Selasky udl_init_resolution(struct udl_softc *sc) 10020867995cSHans Petter Selasky { 10030867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 10040867995cSHans Petter Selasky const uint8_t *buf = udl_modes[sc->sc_cur_mode].mode; 10050867995cSHans Petter Selasky struct udl_cmd_buf *cb; 10060867995cSHans Petter Selasky uint32_t delta; 10070867995cSHans Petter Selasky uint32_t i; 10080867995cSHans Petter Selasky int error; 10090867995cSHans Petter Selasky 10100867995cSHans Petter Selasky /* get new buffer */ 10110867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 10120867995cSHans Petter Selasky if (cb == NULL) 10130867995cSHans Petter Selasky return (EAGAIN); 10140867995cSHans Petter Selasky 10150867995cSHans Petter Selasky /* write resolution values and set video memory offsets */ 10160867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 10170867995cSHans Petter Selasky for (i = 0; i < UDL_MODE_SIZE; i++) 10180867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, i, buf[i]); 10190867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 10200867995cSHans Petter Selasky 10210867995cSHans Petter Selasky udl_init_fb_offsets(cb, 0x000000, 0x000a00, 0x555555, 0x000500); 10220867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 10230867995cSHans Petter Selasky 10240867995cSHans Petter Selasky /* fill screen with black color */ 10250867995cSHans Petter Selasky for (i = 0; i < max; i += delta) { 10260867995cSHans Petter Selasky static const uint8_t udl_black[UDL_CMD_MAX_PIXEL_COUNT * 2] __aligned(4); 10270867995cSHans Petter Selasky 10280867995cSHans Petter Selasky delta = max - i; 10290867995cSHans Petter Selasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 10300867995cSHans Petter Selasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 10310867995cSHans Petter Selasky if (i == 0) 10320867995cSHans Petter Selasky error = udl_cmd_write_buf_le16(sc, udl_black, i, delta / 2, M_WAITOK); 10330867995cSHans Petter Selasky else 10340867995cSHans Petter Selasky error = udl_cmd_buf_copy_le16(sc, 0, i, delta / 2, M_WAITOK); 10350867995cSHans Petter Selasky if (error) 10360867995cSHans Petter Selasky return (error); 10370867995cSHans Petter Selasky } 10380867995cSHans Petter Selasky 10390867995cSHans Petter Selasky /* get new buffer */ 10400867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 10410867995cSHans Petter Selasky if (cb == NULL) 10420867995cSHans Petter Selasky return (EAGAIN); 10430867995cSHans Petter Selasky 10440867995cSHans Petter Selasky /* show framebuffer content */ 10450867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 10460867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 10470867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 10480867995cSHans Petter Selasky return (0); 10490867995cSHans Petter Selasky } 10500867995cSHans Petter Selasky 10510867995cSHans Petter Selasky static void 10520867995cSHans Petter Selasky udl_select_mode(struct udl_softc *sc) 10530867995cSHans Petter Selasky { 10540867995cSHans Petter Selasky struct udl_mode mode; 10550867995cSHans Petter Selasky int index = UDL_MAX_MODES; 10560867995cSHans Petter Selasky int i; 10570867995cSHans Petter Selasky 10580867995cSHans Petter Selasky /* try to get the preferred mode from EDID */ 10590867995cSHans Petter Selasky edid_parse(sc->sc_edid, &sc->sc_edid_info); 10600867995cSHans Petter Selasky #ifdef USB_DEBUG 10610867995cSHans Petter Selasky edid_print(&sc->sc_edid_info); 10620867995cSHans Petter Selasky #endif 10630867995cSHans Petter Selasky if (sc->sc_edid_info.edid_preferred_mode != NULL) { 10640867995cSHans Petter Selasky mode.hz = 10650867995cSHans Petter Selasky (sc->sc_edid_info.edid_preferred_mode->dot_clock * 1000) / 10660867995cSHans Petter Selasky (sc->sc_edid_info.edid_preferred_mode->htotal * 10670867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->vtotal); 10680867995cSHans Petter Selasky mode.clock = 10690867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->dot_clock / 10; 10700867995cSHans Petter Selasky mode.hdisplay = 10710867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->hdisplay; 10720867995cSHans Petter Selasky mode.vdisplay = 10730867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->vdisplay; 10740867995cSHans Petter Selasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, mode.hz, 10750867995cSHans Petter Selasky sc->sc_chip, mode.clock); 10760867995cSHans Petter Selasky sc->sc_cur_mode = index; 10770867995cSHans Petter Selasky } else { 10780867995cSHans Petter Selasky DPRINTF("no preferred mode found!\n"); 10790867995cSHans Petter Selasky } 10800867995cSHans Petter Selasky 10810867995cSHans Petter Selasky if (index == UDL_MAX_MODES) { 108230b44000SHans Petter Selasky DPRINTF("no mode line found\n"); 10830867995cSHans Petter Selasky 10840867995cSHans Petter Selasky i = 0; 10850867995cSHans Petter Selasky while (i < sc->sc_edid_info.edid_nmodes) { 10860867995cSHans Petter Selasky mode.hz = 10870867995cSHans Petter Selasky (sc->sc_edid_info.edid_modes[i].dot_clock * 1000) / 10880867995cSHans Petter Selasky (sc->sc_edid_info.edid_modes[i].htotal * 10890867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].vtotal); 10900867995cSHans Petter Selasky mode.clock = 10910867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].dot_clock / 10; 10920867995cSHans Petter Selasky mode.hdisplay = 10930867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].hdisplay; 10940867995cSHans Petter Selasky mode.vdisplay = 10950867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].vdisplay; 10960867995cSHans Petter Selasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, 10970867995cSHans Petter Selasky mode.hz, sc->sc_chip, mode.clock); 10980867995cSHans Petter Selasky if (index < UDL_MAX_MODES) 10990867995cSHans Petter Selasky if ((sc->sc_cur_mode == UDL_MAX_MODES) || 11000867995cSHans Petter Selasky (index > sc->sc_cur_mode)) 11010867995cSHans Petter Selasky sc->sc_cur_mode = index; 11020867995cSHans Petter Selasky i++; 11030867995cSHans Petter Selasky } 11040867995cSHans Petter Selasky } 11050867995cSHans Petter Selasky /* 11060867995cSHans Petter Selasky * If no mode found use default. 11070867995cSHans Petter Selasky */ 11080867995cSHans Petter Selasky if (sc->sc_cur_mode == UDL_MAX_MODES) 11090867995cSHans Petter Selasky sc->sc_cur_mode = udl_lookup_mode(800, 600, 60, sc->sc_chip, 0); 11100867995cSHans Petter Selasky } 11110867995cSHans Petter Selasky 11120867995cSHans Petter Selasky static int 11130867995cSHans Petter Selasky udl_cmd_write_buf_le16(struct udl_softc *sc, const uint8_t *buf, uint32_t off, 11140867995cSHans Petter Selasky uint8_t pixels, int flags) 11150867995cSHans Petter Selasky { 11160867995cSHans Petter Selasky struct udl_cmd_buf *cb; 11170867995cSHans Petter Selasky 11180867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 11190867995cSHans Petter Selasky if (cb == NULL) 11200867995cSHans Petter Selasky return (EAGAIN); 11210867995cSHans Petter Selasky 11220867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 11230867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 11240867995cSHans Petter Selasky udl_cmd_insert_int_3(cb, off); 11250867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, pixels); 11260867995cSHans Petter Selasky udl_cmd_insert_buf_le16(cb, buf, 2 * pixels); 11270867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 11280867995cSHans Petter Selasky 11290867995cSHans Petter Selasky return (0); 11300867995cSHans Petter Selasky } 11310867995cSHans Petter Selasky 11320867995cSHans Petter Selasky static int 11330867995cSHans Petter Selasky udl_cmd_buf_copy_le16(struct udl_softc *sc, uint32_t src, uint32_t dst, 11340867995cSHans Petter Selasky uint8_t pixels, int flags) 11350867995cSHans Petter Selasky { 11360867995cSHans Petter Selasky struct udl_cmd_buf *cb; 11370867995cSHans Petter Selasky 11380867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 11390867995cSHans Petter Selasky if (cb == NULL) 11400867995cSHans Petter Selasky return (EAGAIN); 11410867995cSHans Petter Selasky 11420867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 11430867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD); 1144b161d698SHans Petter Selasky udl_cmd_insert_int_3(cb, dst); 11450867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, pixels); 1146b161d698SHans Petter Selasky udl_cmd_insert_int_3(cb, src); 11470867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 11480867995cSHans Petter Selasky 11490867995cSHans Petter Selasky return (0); 11500867995cSHans Petter Selasky } 1151