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