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 6361948b25SHans Petter Selasky static SYSCTL_NODE(_hw_usb, OID_AUTO, udl, CTLFLAG_RW, 0, "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 devclass_t udl_devclass; 1360867995cSHans Petter Selasky 1370867995cSHans Petter Selasky static device_method_t udl_methods[] = { 1380867995cSHans Petter Selasky DEVMETHOD(device_probe, udl_probe), 1390867995cSHans Petter Selasky DEVMETHOD(device_attach, udl_attach), 1400867995cSHans Petter Selasky DEVMETHOD(device_detach, udl_detach), 1410867995cSHans Petter Selasky DEVMETHOD(fb_getinfo, udl_fb_getinfo), 1420867995cSHans Petter Selasky DEVMETHOD_END 1430867995cSHans Petter Selasky }; 1440867995cSHans Petter Selasky 1450867995cSHans Petter Selasky static driver_t udl_driver = { 1460867995cSHans Petter Selasky .name = "udl", 1470867995cSHans Petter Selasky .methods = udl_methods, 1480867995cSHans Petter Selasky .size = sizeof(struct udl_softc), 1490867995cSHans Petter Selasky }; 1500867995cSHans Petter Selasky 1510867995cSHans Petter Selasky DRIVER_MODULE(udl, uhub, udl_driver, udl_devclass, NULL, NULL); 1520867995cSHans Petter Selasky MODULE_DEPEND(udl, usb, 1, 1, 1); 1530867995cSHans Petter Selasky MODULE_DEPEND(udl, fbd, 1, 1, 1); 1540867995cSHans Petter Selasky MODULE_DEPEND(udl, videomode, 1, 1, 1); 1550867995cSHans Petter Selasky MODULE_VERSION(udl, 1); 1560867995cSHans Petter Selasky 1570867995cSHans Petter Selasky /* 1580867995cSHans Petter Selasky * Matching devices. 1590867995cSHans Petter Selasky */ 1600867995cSHans Petter Selasky static const STRUCT_USB_HOST_ID udl_devs[] = { 1610867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U, DL120)}, 1620867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U, DL120)}, 1630867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020, DL160)}, 1640867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220, DL165)}, 1650867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60, DL160)}, 1660867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI, DL160)}, 1670867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10, DL120)}, 1680867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI, DLUNK)}, 1690867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008, DL160)}, 1700867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK, DL160)}, 1710867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571, DL160)}, 1720867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061, DL195)}, 1730867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK, DL165)}, 1740867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI, DLUNK)}, 1750867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0, DL120)}, 1760867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV, DL160)}, 177a63352afSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE, DL160)}, 1780867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70, DL125)}, 1790867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2, DLUNK)}, 1804d363380SHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)}, 1814d363380SHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_ITEC, DL165)}, 182*c4e11f22SHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DVI_19, DL165)}, 1830867995cSHans Petter Selasky }; 1840867995cSHans Petter Selasky 18555e11a03SHans Petter Selasky static void 18655e11a03SHans Petter Selasky udl_buffer_init(void *arg) 18755e11a03SHans Petter Selasky { 18855e11a03SHans Petter Selasky mtx_init(&udl_buffer_mtx, "USB", "UDL", MTX_DEF); 18955e11a03SHans Petter Selasky TAILQ_INIT(&udl_buffer_head); 19055e11a03SHans Petter Selasky } 19155e11a03SHans Petter Selasky SYSINIT(udl_buffer_init, SI_SUB_LOCK, SI_ORDER_FIRST, udl_buffer_init, NULL); 19255e11a03SHans Petter Selasky 19355e11a03SHans Petter Selasky CTASSERT(sizeof(struct udl_buffer) < PAGE_SIZE); 19455e11a03SHans Petter Selasky 19555e11a03SHans Petter Selasky static void * 19655e11a03SHans Petter Selasky udl_buffer_alloc(uint32_t size) 19755e11a03SHans Petter Selasky { 19855e11a03SHans Petter Selasky struct udl_buffer *buf; 19955e11a03SHans Petter Selasky mtx_lock(&udl_buffer_mtx); 20055e11a03SHans Petter Selasky TAILQ_FOREACH(buf, &udl_buffer_head, entry) { 20155e11a03SHans Petter Selasky if (buf->size == size) { 20255e11a03SHans Petter Selasky TAILQ_REMOVE(&udl_buffer_head, buf, entry); 20355e11a03SHans Petter Selasky break; 20455e11a03SHans Petter Selasky } 20555e11a03SHans Petter Selasky } 20655e11a03SHans Petter Selasky mtx_unlock(&udl_buffer_mtx); 20755e11a03SHans Petter Selasky if (buf != NULL) { 208a998d103SHans Petter Selasky uint8_t *ptr = ((uint8_t *)buf) - size; 20955e11a03SHans Petter Selasky /* wipe and recycle buffer */ 210a998d103SHans Petter Selasky memset(ptr, 0, size); 211a998d103SHans Petter Selasky /* return buffer pointer */ 212a998d103SHans Petter Selasky return (ptr); 21355e11a03SHans Petter Selasky } 21455e11a03SHans Petter Selasky /* allocate new buffer */ 215a998d103SHans Petter Selasky return (malloc(size + sizeof(*buf), M_USB_DL, M_WAITOK | M_ZERO)); 21655e11a03SHans Petter Selasky } 21755e11a03SHans Petter Selasky 21855e11a03SHans Petter Selasky static void 21955e11a03SHans Petter Selasky udl_buffer_free(void *_buf, uint32_t size) 22055e11a03SHans Petter Selasky { 22155e11a03SHans Petter Selasky struct udl_buffer *buf; 22255e11a03SHans Petter Selasky 223a998d103SHans Petter Selasky /* check for NULL pointer */ 224a998d103SHans Petter Selasky if (_buf == NULL) 22555e11a03SHans Petter Selasky return; 226a998d103SHans Petter Selasky /* compute pointer to recycle list */ 227a998d103SHans Petter Selasky buf = (struct udl_buffer *)(((uint8_t *)_buf) + size); 22855e11a03SHans Petter Selasky 22955e11a03SHans Petter Selasky /* 23055e11a03SHans Petter Selasky * Memory mapped buffers should never be freed. 23155e11a03SHans Petter Selasky * Put display buffer into a recycle list. 23255e11a03SHans Petter Selasky */ 23355e11a03SHans Petter Selasky mtx_lock(&udl_buffer_mtx); 23455e11a03SHans Petter Selasky buf->size = size; 23555e11a03SHans Petter Selasky TAILQ_INSERT_TAIL(&udl_buffer_head, buf, entry); 23655e11a03SHans Petter Selasky mtx_unlock(&udl_buffer_mtx); 23755e11a03SHans Petter Selasky } 23855e11a03SHans Petter Selasky 2390867995cSHans Petter Selasky static uint32_t 2400867995cSHans Petter Selasky udl_get_fb_size(struct udl_softc *sc) 2410867995cSHans Petter Selasky { 2420867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2430867995cSHans Petter Selasky 2440867995cSHans Petter Selasky return ((uint32_t)udl_modes[i].hdisplay * 2450867995cSHans Petter Selasky (uint32_t)udl_modes[i].vdisplay * 2); 2460867995cSHans Petter Selasky } 2470867995cSHans Petter Selasky 2480867995cSHans Petter Selasky static uint32_t 2490867995cSHans Petter Selasky udl_get_fb_width(struct udl_softc *sc) 2500867995cSHans Petter Selasky { 2510867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2520867995cSHans Petter Selasky 2534464c5e5SHans Petter Selasky return (udl_modes[i].hdisplay); 2540867995cSHans Petter Selasky } 2550867995cSHans Petter Selasky 2560867995cSHans Petter Selasky static uint32_t 2570867995cSHans Petter Selasky udl_get_fb_height(struct udl_softc *sc) 2580867995cSHans Petter Selasky { 2590867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2600867995cSHans Petter Selasky 2614464c5e5SHans Petter Selasky return (udl_modes[i].vdisplay); 2620867995cSHans Petter Selasky } 2630867995cSHans Petter Selasky 2640867995cSHans Petter Selasky static uint32_t 2650867995cSHans Petter Selasky udl_get_fb_hz(struct udl_softc *sc) 2660867995cSHans Petter Selasky { 2670867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2680867995cSHans Petter Selasky 2690867995cSHans Petter Selasky return (udl_modes[i].hz); 2700867995cSHans Petter Selasky } 2710867995cSHans Petter Selasky 2720867995cSHans Petter Selasky static void 2730867995cSHans Petter Selasky udl_callout(void *arg) 2740867995cSHans Petter Selasky { 2750867995cSHans Petter Selasky struct udl_softc *sc = arg; 2760867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 27761948b25SHans Petter Selasky int fps; 2780867995cSHans Petter Selasky 2790867995cSHans Petter Selasky if (sc->sc_power_save == 0) { 28061948b25SHans Petter Selasky fps = udl_fps; 28161948b25SHans Petter Selasky 28261948b25SHans Petter Selasky /* figure out number of frames per second */ 28361948b25SHans Petter Selasky if (fps < UDL_FPS_MIN) 28461948b25SHans Petter Selasky fps = UDL_FPS_MIN; 28561948b25SHans Petter Selasky else if (fps > UDL_FPS_MAX) 28661948b25SHans Petter Selasky fps = UDL_FPS_MAX; 28761948b25SHans Petter Selasky 2880867995cSHans Petter Selasky if (sc->sc_sync_off >= max) 2890867995cSHans Petter Selasky sc->sc_sync_off = 0; 2900867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 2910867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 29261948b25SHans Petter Selasky } else { 29361948b25SHans Petter Selasky fps = 1; 2940867995cSHans Petter Selasky } 29561948b25SHans Petter Selasky callout_reset(&sc->sc_callout, hz / fps, &udl_callout, sc); 2960867995cSHans Petter Selasky } 2970867995cSHans Petter Selasky 2980867995cSHans Petter Selasky static int 2990867995cSHans Petter Selasky udl_probe(device_t dev) 3000867995cSHans Petter Selasky { 3010867995cSHans Petter Selasky struct usb_attach_arg *uaa = device_get_ivars(dev); 3020867995cSHans Petter Selasky 3030867995cSHans Petter Selasky if (uaa->usb_mode != USB_MODE_HOST) 3040867995cSHans Petter Selasky return (ENXIO); 3050867995cSHans Petter Selasky if (uaa->info.bConfigIndex != 0) 3060867995cSHans Petter Selasky return (ENXIO); 3070867995cSHans Petter Selasky if (uaa->info.bIfaceIndex != 0) 3080867995cSHans Petter Selasky return (ENXIO); 3090867995cSHans Petter Selasky 3100867995cSHans Petter Selasky return (usbd_lookup_id_by_uaa(udl_devs, sizeof(udl_devs), uaa)); 3110867995cSHans Petter Selasky } 3120867995cSHans Petter Selasky 3130867995cSHans Petter Selasky static int 3140867995cSHans Petter Selasky udl_attach(device_t dev) 3150867995cSHans Petter Selasky { 3160867995cSHans Petter Selasky struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 3170867995cSHans Petter Selasky struct sysctl_oid *tree = device_get_sysctl_tree(dev); 3180867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 3190867995cSHans Petter Selasky struct usb_attach_arg *uaa = device_get_ivars(dev); 3200867995cSHans Petter Selasky int error; 3210867995cSHans Petter Selasky int i; 3220867995cSHans Petter Selasky 3230867995cSHans Petter Selasky device_set_usb_desc(dev); 3240867995cSHans Petter Selasky 3250867995cSHans Petter Selasky mtx_init(&sc->sc_mtx, "UDL lock", NULL, MTX_DEF); 3260867995cSHans Petter Selasky cv_init(&sc->sc_cv, "UDLCV"); 3270867995cSHans Petter Selasky callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 3280867995cSHans Petter Selasky sc->sc_udev = uaa->device; 3290867995cSHans Petter Selasky 3300867995cSHans Petter Selasky error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 3310867995cSHans Petter Selasky sc->sc_xfer, udl_config, UDL_N_TRANSFER, sc, &sc->sc_mtx); 3320867995cSHans Petter Selasky 3330867995cSHans Petter Selasky if (error) { 3340867995cSHans Petter Selasky DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error)); 3350867995cSHans Petter Selasky goto detach; 3360867995cSHans Petter Selasky } 3370867995cSHans Petter Selasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_0], &sc->sc_xfer_head[0]); 3380867995cSHans Petter Selasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_1], &sc->sc_xfer_head[1]); 3390867995cSHans Petter Selasky 3400867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_xfer_head[0]); 3410867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_xfer_head[1]); 3420867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_cmd_buf_free); 3430867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_cmd_buf_pending); 3440867995cSHans Petter Selasky 3450867995cSHans Petter Selasky sc->sc_def_chip = -1; 3460867995cSHans Petter Selasky sc->sc_chip = USB_GET_DRIVER_INFO(uaa); 3470867995cSHans Petter Selasky sc->sc_def_mode = -1; 3480867995cSHans Petter Selasky sc->sc_cur_mode = UDL_MAX_MODES; 3490867995cSHans Petter Selasky 3500867995cSHans Petter Selasky /* Allow chip ID to be overwritten */ 3510867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid_force", 3520867995cSHans Petter Selasky CTLFLAG_RWTUN, &sc->sc_def_chip, 0, "chip ID"); 3530867995cSHans Petter Selasky 3540867995cSHans Petter Selasky /* Export current chip ID */ 3550867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid", 3560867995cSHans Petter Selasky CTLFLAG_RD, &sc->sc_chip, 0, "chip ID"); 3570867995cSHans Petter Selasky 3580867995cSHans Petter Selasky if (sc->sc_def_chip > -1 && sc->sc_def_chip <= DLMAX) { 3590867995cSHans Petter Selasky device_printf(dev, "Forcing chip ID to 0x%04x\n", sc->sc_def_chip); 3600867995cSHans Petter Selasky sc->sc_chip = sc->sc_def_chip; 3610867995cSHans Petter Selasky } 3620867995cSHans Petter Selasky /* 3630867995cSHans Petter Selasky * The product might have more than one chip 3640867995cSHans Petter Selasky */ 3650867995cSHans Petter Selasky if (sc->sc_chip == DLUNK) 3660867995cSHans Petter Selasky udl_select_chip(sc, uaa); 3670867995cSHans Petter Selasky 3680867995cSHans Petter Selasky for (i = 0; i != UDL_CMD_MAX_BUFFERS; i++) { 3690867995cSHans Petter Selasky struct udl_cmd_buf *cb = &sc->sc_cmd_buf_temp[i]; 3700867995cSHans Petter Selasky 3710867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 3720867995cSHans Petter Selasky } 3730867995cSHans Petter Selasky 3740867995cSHans Petter Selasky /* 3750867995cSHans Petter Selasky * Initialize chip. 3760867995cSHans Petter Selasky */ 3770867995cSHans Petter Selasky error = udl_init_chip(sc); 3780867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 3790867995cSHans Petter Selasky goto detach; 3800867995cSHans Petter Selasky 3810867995cSHans Petter Selasky /* 3820867995cSHans Petter Selasky * Select edid mode. 3830867995cSHans Petter Selasky */ 3840867995cSHans Petter Selasky udl_select_mode(sc); 3850867995cSHans Petter Selasky 3860867995cSHans Petter Selasky /* Allow default mode to be overwritten */ 3870867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode_force", 3880867995cSHans Petter Selasky CTLFLAG_RWTUN, &sc->sc_def_mode, 0, "mode"); 3890867995cSHans Petter Selasky 3900867995cSHans Petter Selasky /* Export current mode */ 3910867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode", 3920867995cSHans Petter Selasky CTLFLAG_RD, &sc->sc_cur_mode, 0, "mode"); 3930867995cSHans Petter Selasky 3940867995cSHans Petter Selasky i = sc->sc_def_mode; 3950867995cSHans Petter Selasky if (i > -1 && i < UDL_MAX_MODES) { 3960867995cSHans Petter Selasky if (udl_modes[i].chip <= sc->sc_chip) { 3970867995cSHans Petter Selasky device_printf(dev, "Forcing mode to %d\n", i); 3980867995cSHans Petter Selasky sc->sc_cur_mode = i; 3990867995cSHans Petter Selasky } 4000867995cSHans Petter Selasky } 4010867995cSHans Petter Selasky /* Printout current mode */ 4020867995cSHans Petter Selasky device_printf(dev, "Mode selected %dx%d @ %dHz\n", 4030867995cSHans Petter Selasky (int)udl_get_fb_width(sc), 4040867995cSHans Petter Selasky (int)udl_get_fb_height(sc), 4050867995cSHans Petter Selasky (int)udl_get_fb_hz(sc)); 4060867995cSHans Petter Selasky 4070867995cSHans Petter Selasky udl_init_resolution(sc); 4080867995cSHans Petter Selasky 4090867995cSHans Petter Selasky /* Allocate frame buffer */ 4100867995cSHans Petter Selasky udl_fbmem_alloc(sc); 4110867995cSHans Petter Selasky 4120867995cSHans Petter Selasky UDL_LOCK(sc); 4130867995cSHans Petter Selasky udl_callout(sc); 4140867995cSHans Petter Selasky UDL_UNLOCK(sc); 4150867995cSHans Petter Selasky 4160867995cSHans Petter Selasky sc->sc_fb_info.fb_name = device_get_nameunit(dev); 4170867995cSHans Petter Selasky sc->sc_fb_info.fb_size = sc->sc_fb_size; 4180867995cSHans Petter Selasky sc->sc_fb_info.fb_bpp = 16; 4190867995cSHans Petter Selasky sc->sc_fb_info.fb_depth = 16; 4200867995cSHans Petter Selasky sc->sc_fb_info.fb_width = udl_get_fb_width(sc); 4210867995cSHans Petter Selasky sc->sc_fb_info.fb_height = udl_get_fb_height(sc); 4220867995cSHans Petter Selasky sc->sc_fb_info.fb_stride = sc->sc_fb_info.fb_width * 2; 423b161d698SHans Petter Selasky sc->sc_fb_info.fb_pbase = 0; 4240867995cSHans Petter Selasky sc->sc_fb_info.fb_vbase = (uintptr_t)sc->sc_fb_addr; 42500ad411aSHans Petter Selasky sc->sc_fb_info.fb_priv = sc; 42600ad411aSHans Petter Selasky sc->sc_fb_info.setblankmode = &udl_fb_setblankmode; 4270867995cSHans Petter Selasky 4280867995cSHans Petter Selasky sc->sc_fbdev = device_add_child(dev, "fbd", -1); 4290867995cSHans Petter Selasky if (sc->sc_fbdev == NULL) 4300867995cSHans Petter Selasky goto detach; 4310867995cSHans Petter Selasky if (device_probe_and_attach(sc->sc_fbdev) != 0) 4320867995cSHans Petter Selasky goto detach; 4330867995cSHans Petter Selasky 4340867995cSHans Petter Selasky return (0); 4350867995cSHans Petter Selasky 4360867995cSHans Petter Selasky detach: 4370867995cSHans Petter Selasky udl_detach(dev); 4380867995cSHans Petter Selasky 4390867995cSHans Petter Selasky return (ENXIO); 4400867995cSHans Petter Selasky } 4410867995cSHans Petter Selasky 4420867995cSHans Petter Selasky static int 4430867995cSHans Petter Selasky udl_detach(device_t dev) 4440867995cSHans Petter Selasky { 4450867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 4460867995cSHans Petter Selasky 447d3bf5efcSHans Petter Selasky /* delete all child devices */ 448d3bf5efcSHans Petter Selasky device_delete_children(dev); 4490867995cSHans Petter Selasky 4500867995cSHans Petter Selasky UDL_LOCK(sc); 4510867995cSHans Petter Selasky sc->sc_gone = 1; 4520867995cSHans Petter Selasky callout_stop(&sc->sc_callout); 4530867995cSHans Petter Selasky UDL_UNLOCK(sc); 4540867995cSHans Petter Selasky 4550867995cSHans Petter Selasky usbd_transfer_unsetup(sc->sc_xfer, UDL_N_TRANSFER); 4560867995cSHans Petter Selasky 4570867995cSHans Petter Selasky callout_drain(&sc->sc_callout); 4580867995cSHans Petter Selasky 4590867995cSHans Petter Selasky mtx_destroy(&sc->sc_mtx); 4600867995cSHans Petter Selasky cv_destroy(&sc->sc_cv); 4610867995cSHans Petter Selasky 46255e11a03SHans Petter Selasky /* put main framebuffer into a recycle list, if any */ 46355e11a03SHans Petter Selasky udl_buffer_free(sc->sc_fb_addr, sc->sc_fb_size); 46455e11a03SHans Petter Selasky 46555e11a03SHans Petter Selasky /* free shadow framebuffer memory, if any */ 46655e11a03SHans Petter Selasky free(sc->sc_fb_copy, M_USB_DL); 4670867995cSHans Petter Selasky 4680867995cSHans Petter Selasky return (0); 4690867995cSHans Petter Selasky } 4700867995cSHans Petter Selasky 4710867995cSHans Petter Selasky static struct fb_info * 4720867995cSHans Petter Selasky udl_fb_getinfo(device_t dev) 4730867995cSHans Petter Selasky { 4740867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 4750867995cSHans Petter Selasky 4760867995cSHans Petter Selasky return (&sc->sc_fb_info); 4770867995cSHans Petter Selasky } 4780867995cSHans Petter Selasky 4790867995cSHans Petter Selasky static int 48000ad411aSHans Petter Selasky udl_fb_setblankmode(void *arg, int mode) 4810867995cSHans Petter Selasky { 48200ad411aSHans Petter Selasky struct udl_softc *sc = arg; 4830867995cSHans Petter Selasky 4840867995cSHans Petter Selasky switch (mode) { 4850867995cSHans Petter Selasky case V_DISPLAY_ON: 4860867995cSHans Petter Selasky udl_power_save(sc, 1, M_WAITOK); 4870867995cSHans Petter Selasky break; 4880867995cSHans Petter Selasky case V_DISPLAY_BLANK: 4890867995cSHans Petter Selasky udl_power_save(sc, 1, M_WAITOK); 4900867995cSHans Petter Selasky if (sc->sc_fb_addr != 0) { 4910867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 4920867995cSHans Petter Selasky 4930867995cSHans Petter Selasky memset((void *)sc->sc_fb_addr, 0, max); 4940867995cSHans Petter Selasky } 4950867995cSHans Petter Selasky break; 4960867995cSHans Petter Selasky case V_DISPLAY_STAND_BY: 4970867995cSHans Petter Selasky case V_DISPLAY_SUSPEND: 4980867995cSHans Petter Selasky udl_power_save(sc, 0, M_WAITOK); 4990867995cSHans Petter Selasky break; 5000867995cSHans Petter Selasky } 5010867995cSHans Petter Selasky return (0); 5020867995cSHans Petter Selasky } 5030867995cSHans Petter Selasky 5040867995cSHans Petter Selasky static struct udl_cmd_buf * 505b41892e1SHans Petter Selasky udl_cmd_buf_alloc_locked(struct udl_softc *sc, int flags) 5060867995cSHans Petter Selasky { 5070867995cSHans Petter Selasky struct udl_cmd_buf *cb; 5080867995cSHans Petter Selasky 5090867995cSHans Petter Selasky while ((cb = TAILQ_FIRST(&sc->sc_cmd_buf_free)) == NULL) { 5100867995cSHans Petter Selasky if (flags != M_WAITOK) 5110867995cSHans Petter Selasky break; 5120867995cSHans Petter Selasky cv_wait(&sc->sc_cv, &sc->sc_mtx); 5130867995cSHans Petter Selasky } 5140867995cSHans Petter Selasky if (cb != NULL) { 5150867995cSHans Petter Selasky TAILQ_REMOVE(&sc->sc_cmd_buf_free, cb, entry); 5160867995cSHans Petter Selasky cb->off = 0; 5170867995cSHans Petter Selasky } 518b41892e1SHans Petter Selasky return (cb); 519b41892e1SHans Petter Selasky } 520b41892e1SHans Petter Selasky 521b41892e1SHans Petter Selasky static struct udl_cmd_buf * 522b41892e1SHans Petter Selasky udl_cmd_buf_alloc(struct udl_softc *sc, int flags) 523b41892e1SHans Petter Selasky { 524b41892e1SHans Petter Selasky struct udl_cmd_buf *cb; 525b41892e1SHans Petter Selasky 526b41892e1SHans Petter Selasky UDL_LOCK(sc); 527b41892e1SHans Petter Selasky cb = udl_cmd_buf_alloc_locked(sc, flags); 5280867995cSHans Petter Selasky UDL_UNLOCK(sc); 5290867995cSHans Petter Selasky return (cb); 5300867995cSHans Petter Selasky } 5310867995cSHans Petter Selasky 5320867995cSHans Petter Selasky static void 5330867995cSHans Petter Selasky udl_cmd_buf_send(struct udl_softc *sc, struct udl_cmd_buf *cb) 5340867995cSHans Petter Selasky { 5350867995cSHans Petter Selasky UDL_LOCK(sc); 5360867995cSHans Petter Selasky if (sc->sc_gone) { 5370867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 5380867995cSHans Petter Selasky } else { 5390867995cSHans Petter Selasky /* mark end of command stack */ 5400867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 5410867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_EOC); 5420867995cSHans Petter Selasky 5430867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_pending, cb, entry); 5440867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 5450867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 5460867995cSHans Petter Selasky } 5470867995cSHans Petter Selasky UDL_UNLOCK(sc); 5480867995cSHans Petter Selasky } 5490867995cSHans Petter Selasky 5500867995cSHans Petter Selasky static struct udl_cmd_buf * 551b41892e1SHans Petter Selasky udl_fb_synchronize_locked(struct udl_softc *sc) 5520867995cSHans Petter Selasky { 5530867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 5540867995cSHans Petter Selasky 555b161d698SHans Petter Selasky /* check if framebuffer is not ready */ 556b161d698SHans Petter Selasky if (sc->sc_fb_addr == NULL || 557b161d698SHans Petter Selasky sc->sc_fb_copy == NULL) 558b161d698SHans Petter Selasky return (NULL); 559b161d698SHans Petter Selasky 5600867995cSHans Petter Selasky while (sc->sc_sync_off < max) { 5610867995cSHans Petter Selasky uint32_t delta = max - sc->sc_sync_off; 5620867995cSHans Petter Selasky 5630867995cSHans Petter Selasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 5640867995cSHans Petter Selasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 5650867995cSHans Petter Selasky if (bcmp(sc->sc_fb_addr + sc->sc_sync_off, sc->sc_fb_copy + sc->sc_sync_off, delta) != 0) { 5660867995cSHans Petter Selasky struct udl_cmd_buf *cb; 5670867995cSHans Petter Selasky 568b41892e1SHans Petter Selasky cb = udl_cmd_buf_alloc_locked(sc, M_NOWAIT); 5690867995cSHans Petter Selasky if (cb == NULL) 5700867995cSHans Petter Selasky goto done; 5710867995cSHans Petter Selasky memcpy(sc->sc_fb_copy + sc->sc_sync_off, 5720867995cSHans Petter Selasky sc->sc_fb_addr + sc->sc_sync_off, delta); 5730867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 5740867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 5750867995cSHans Petter Selasky udl_cmd_insert_int_3(cb, sc->sc_sync_off); 5760867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, delta / 2); 5770867995cSHans Petter Selasky udl_cmd_insert_buf_le16(cb, sc->sc_fb_copy + sc->sc_sync_off, delta); 5780867995cSHans Petter Selasky sc->sc_sync_off += delta; 5790867995cSHans Petter Selasky return (cb); 5800867995cSHans Petter Selasky } else { 5810867995cSHans Petter Selasky sc->sc_sync_off += delta; 5820867995cSHans Petter Selasky } 5830867995cSHans Petter Selasky } 5840867995cSHans Petter Selasky done: 5850867995cSHans Petter Selasky return (NULL); 5860867995cSHans Petter Selasky } 5870867995cSHans Petter Selasky 5880867995cSHans Petter Selasky static void 5890867995cSHans Petter Selasky udl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 5900867995cSHans Petter Selasky { 5910867995cSHans Petter Selasky struct udl_softc *sc = usbd_xfer_softc(xfer); 5920867995cSHans Petter Selasky struct udl_cmd_head *phead = usbd_xfer_get_priv(xfer); 5930867995cSHans Petter Selasky struct udl_cmd_buf *cb; 5940867995cSHans Petter Selasky unsigned i; 5950867995cSHans Petter Selasky 5960867995cSHans Petter Selasky switch (USB_GET_STATE(xfer)) { 5970867995cSHans Petter Selasky case USB_ST_TRANSFERRED: 5980867995cSHans Petter Selasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 5990867995cSHans Petter Selasky case USB_ST_SETUP: 6000867995cSHans Petter Selasky tr_setup: 6010867995cSHans Petter Selasky for (i = 0; i != UDL_CMD_MAX_FRAMES; i++) { 6020867995cSHans Petter Selasky cb = TAILQ_FIRST(&sc->sc_cmd_buf_pending); 6030867995cSHans Petter Selasky if (cb == NULL) { 604b41892e1SHans Petter Selasky cb = udl_fb_synchronize_locked(sc); 6050867995cSHans Petter Selasky if (cb == NULL) 6060867995cSHans Petter Selasky break; 607b161d698SHans Petter Selasky } else { 6080867995cSHans Petter Selasky TAILQ_REMOVE(&sc->sc_cmd_buf_pending, cb, entry); 609b161d698SHans Petter Selasky } 6100867995cSHans Petter Selasky TAILQ_INSERT_TAIL(phead, cb, entry); 6110867995cSHans Petter Selasky usbd_xfer_set_frame_data(xfer, i, cb->buf, cb->off); 6120867995cSHans Petter Selasky } 6130867995cSHans Petter Selasky if (i != 0) { 6140867995cSHans Petter Selasky usbd_xfer_set_frames(xfer, i); 6150867995cSHans Petter Selasky usbd_transfer_submit(xfer); 6160867995cSHans Petter Selasky } 6170867995cSHans Petter Selasky break; 6180867995cSHans Petter Selasky default: 6190867995cSHans Petter Selasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 6200867995cSHans Petter Selasky if (error != USB_ERR_CANCELLED) { 6210867995cSHans Petter Selasky /* try clear stall first */ 6220867995cSHans Petter Selasky usbd_xfer_set_stall(xfer); 6230867995cSHans Petter Selasky goto tr_setup; 6240867995cSHans Petter Selasky } 6250867995cSHans Petter Selasky break; 6260867995cSHans Petter Selasky } 6270867995cSHans Petter Selasky /* wakeup any waiters */ 6280867995cSHans Petter Selasky cv_signal(&sc->sc_cv); 6290867995cSHans Petter Selasky } 6300867995cSHans Petter Selasky 6310867995cSHans Petter Selasky static int 6320867995cSHans Petter Selasky udl_power_save(struct udl_softc *sc, int on, int flags) 6330867995cSHans Petter Selasky { 6340867995cSHans Petter Selasky struct udl_cmd_buf *cb; 6350867995cSHans Petter Selasky 6360867995cSHans Petter Selasky /* get new buffer */ 6370867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 6380867995cSHans Petter Selasky if (cb == NULL) 6390867995cSHans Petter Selasky return (EAGAIN); 6400867995cSHans Petter Selasky 6410867995cSHans Petter Selasky DPRINTF("screen %s\n", on ? "ON" : "OFF"); 6420867995cSHans Petter Selasky 6430867995cSHans Petter Selasky sc->sc_power_save = on ? 0 : 1; 6440867995cSHans Petter Selasky 6450867995cSHans Petter Selasky if (on) 6460867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 6470867995cSHans Petter Selasky else 6480867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF); 6490867995cSHans Petter Selasky 6500867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 6510867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 6520867995cSHans Petter Selasky return (0); 6530867995cSHans Petter Selasky } 6540867995cSHans Petter Selasky 6550867995cSHans Petter Selasky static int 6560867995cSHans Petter Selasky udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r, 6570867995cSHans Petter Selasky uint16_t index, uint16_t value, uint8_t *buf, size_t len) 6580867995cSHans Petter Selasky { 6590867995cSHans Petter Selasky usb_device_request_t req; 6600867995cSHans Petter Selasky int error; 6610867995cSHans Petter Selasky 6620867995cSHans Petter Selasky req.bmRequestType = rt; 6630867995cSHans Petter Selasky req.bRequest = r; 6640867995cSHans Petter Selasky USETW(req.wIndex, index); 6650867995cSHans Petter Selasky USETW(req.wValue, value); 6660867995cSHans Petter Selasky USETW(req.wLength, len); 6670867995cSHans Petter Selasky 6680867995cSHans Petter Selasky error = usbd_do_request_flags(sc->sc_udev, NULL, 6690867995cSHans Petter Selasky &req, buf, 0, NULL, USB_DEFAULT_TIMEOUT); 6700867995cSHans Petter Selasky 6710867995cSHans Petter Selasky DPRINTF("%s\n", usbd_errstr(error)); 6720867995cSHans Petter Selasky 6730867995cSHans Petter Selasky return (error); 6740867995cSHans Petter Selasky } 6750867995cSHans Petter Selasky 6760867995cSHans Petter Selasky static int 6770867995cSHans Petter Selasky udl_poll(struct udl_softc *sc, uint32_t *buf) 6780867995cSHans Petter Selasky { 6790867995cSHans Petter Selasky uint32_t lbuf; 6800867995cSHans Petter Selasky int error; 6810867995cSHans Petter Selasky 6820867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6830867995cSHans Petter Selasky UDL_CTRL_CMD_POLL, 0x0000, 0x0000, (uint8_t *)&lbuf, sizeof(lbuf)); 6840867995cSHans Petter Selasky if (error == USB_ERR_NORMAL_COMPLETION) 6850867995cSHans Petter Selasky *buf = le32toh(lbuf); 6860867995cSHans Petter Selasky return (error); 6870867995cSHans Petter Selasky } 6880867995cSHans Petter Selasky 6890867995cSHans Petter Selasky static int 6900867995cSHans Petter Selasky udl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf) 6910867995cSHans Petter Selasky { 6920867995cSHans Petter Selasky uint8_t lbuf[1]; 6930867995cSHans Petter Selasky int error; 6940867995cSHans Petter Selasky 6950867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6960867995cSHans Petter Selasky UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1); 6970867995cSHans Petter Selasky if (error == USB_ERR_NORMAL_COMPLETION) 6980867995cSHans Petter Selasky *buf = *(uint8_t *)lbuf; 6990867995cSHans Petter Selasky return (error); 7000867995cSHans Petter Selasky } 7010867995cSHans Petter Selasky 7020867995cSHans Petter Selasky static int 7030867995cSHans Petter Selasky udl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf) 7040867995cSHans Petter Selasky { 7050867995cSHans Petter Selasky int error; 7060867995cSHans Petter Selasky 7070867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 7080867995cSHans Petter Selasky UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1); 7090867995cSHans Petter Selasky return (error); 7100867995cSHans Petter Selasky } 7110867995cSHans Petter Selasky 7120867995cSHans Petter Selasky static int 7130867995cSHans Petter Selasky udl_read_edid(struct udl_softc *sc, uint8_t *buf) 7140867995cSHans Petter Selasky { 7150867995cSHans Petter Selasky uint8_t lbuf[64]; 7160867995cSHans Petter Selasky uint16_t offset; 7170867995cSHans Petter Selasky int error; 7180867995cSHans Petter Selasky 7190867995cSHans Petter Selasky offset = 0; 7200867995cSHans Petter Selasky 7210867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 7220867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 7230867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 7240867995cSHans Petter Selasky goto fail; 7250867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 63); 7260867995cSHans Petter Selasky offset += 63; 7270867995cSHans Petter Selasky 7280867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 7290867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 7300867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 7310867995cSHans Petter Selasky goto fail; 7320867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 63); 7330867995cSHans Petter Selasky offset += 63; 7340867995cSHans Petter Selasky 7350867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 7360867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3); 7370867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 7380867995cSHans Petter Selasky goto fail; 7390867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 2); 7400867995cSHans Petter Selasky fail: 7410867995cSHans Petter Selasky return (error); 7420867995cSHans Petter Selasky } 7430867995cSHans Petter Selasky 7440867995cSHans Petter Selasky static uint8_t 7450867995cSHans Petter Selasky udl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t hz, 7460867995cSHans Petter Selasky uint16_t chip, uint32_t clock) 7470867995cSHans Petter Selasky { 7480867995cSHans Petter Selasky uint8_t idx; 7490867995cSHans Petter Selasky 7500867995cSHans Petter Selasky /* 7510867995cSHans Petter Selasky * Check first if we have a matching mode with pixelclock 7520867995cSHans Petter Selasky */ 7530867995cSHans Petter Selasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 7540867995cSHans Petter Selasky if ((udl_modes[idx].hdisplay == hdisplay) && 7550867995cSHans Petter Selasky (udl_modes[idx].vdisplay == vdisplay) && 7560867995cSHans Petter Selasky (udl_modes[idx].clock == clock) && 7570867995cSHans Petter Selasky (udl_modes[idx].chip <= chip)) { 7580867995cSHans Petter Selasky return (idx); 7590867995cSHans Petter Selasky } 7600867995cSHans Petter Selasky } 7610867995cSHans Petter Selasky 7620867995cSHans Petter Selasky /* 7630867995cSHans Petter Selasky * If not, check for matching mode with update frequency 7640867995cSHans Petter Selasky */ 7650867995cSHans Petter Selasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 7660867995cSHans Petter Selasky if ((udl_modes[idx].hdisplay == hdisplay) && 7670867995cSHans Petter Selasky (udl_modes[idx].vdisplay == vdisplay) && 7680867995cSHans Petter Selasky (udl_modes[idx].hz == hz) && 7690867995cSHans Petter Selasky (udl_modes[idx].chip <= chip)) { 7700867995cSHans Petter Selasky return (idx); 7710867995cSHans Petter Selasky } 7720867995cSHans Petter Selasky } 7730867995cSHans Petter Selasky return (idx); 7740867995cSHans Petter Selasky } 7750867995cSHans Petter Selasky 7760867995cSHans Petter Selasky static void 7770867995cSHans Petter Selasky udl_select_chip(struct udl_softc *sc, struct usb_attach_arg *uaa) 7780867995cSHans Petter Selasky { 7790867995cSHans Petter Selasky const char *pserial; 7800867995cSHans Petter Selasky 7810867995cSHans Petter Selasky pserial = usb_get_serial(uaa->device); 7820867995cSHans Petter Selasky 7830867995cSHans Petter Selasky sc->sc_chip = DL120; 7840867995cSHans Petter Selasky 7850867995cSHans Petter Selasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 7860867995cSHans Petter Selasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_WSDVI)) { 7870867995cSHans Petter Selasky 7880867995cSHans Petter Selasky /* 7890867995cSHans Petter Selasky * WS Tech DVI is DL120 or DL160. All deviced uses the 7900867995cSHans Petter Selasky * same revision (0.04) so iSerialNumber must be used 7910867995cSHans Petter Selasky * to determin which chip it is. 7920867995cSHans Petter Selasky */ 7930867995cSHans Petter Selasky 7940867995cSHans Petter Selasky if (strlen(pserial) > 7) { 7950867995cSHans Petter Selasky if (strncmp(pserial, "0198-13", 7) == 0) 7960867995cSHans Petter Selasky sc->sc_chip = DL160; 7970867995cSHans Petter Selasky } 7980867995cSHans Petter Selasky DPRINTF("iSerialNumber (%s) used to select chip (%d)\n", 7990867995cSHans Petter Selasky pserial, sc->sc_chip); 8000867995cSHans Petter Selasky } 8010867995cSHans Petter Selasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 8020867995cSHans Petter Selasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_SWDVI)) { 8030867995cSHans Petter Selasky 8040867995cSHans Petter Selasky /* 8050867995cSHans Petter Selasky * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision 8060867995cSHans Petter Selasky * can be used to differ between DL1x0 and DL1x5. Minor to 8070867995cSHans Petter Selasky * differ between DL1x5. iSerialNumber seems not to be uniqe. 8080867995cSHans Petter Selasky */ 8090867995cSHans Petter Selasky 8100867995cSHans Petter Selasky sc->sc_chip = DL160; 8110867995cSHans Petter Selasky 8120867995cSHans Petter Selasky if (uaa->info.bcdDevice >= 0x100) { 8130867995cSHans Petter Selasky sc->sc_chip = DL165; 8140867995cSHans Petter Selasky if (uaa->info.bcdDevice == 0x104) 8150867995cSHans Petter Selasky sc->sc_chip = DL195; 8160867995cSHans Petter Selasky if (uaa->info.bcdDevice == 0x108) 8170867995cSHans Petter Selasky sc->sc_chip = DL125; 8180867995cSHans Petter Selasky } 8190867995cSHans Petter Selasky DPRINTF("bcdDevice (%02x) used to select chip (%d)\n", 8200867995cSHans Petter Selasky uaa->info.bcdDevice, sc->sc_chip); 8210867995cSHans Petter Selasky } 8220867995cSHans Petter Selasky } 8230867995cSHans Petter Selasky 8240867995cSHans Petter Selasky static int 8250867995cSHans Petter Selasky udl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len) 8260867995cSHans Petter Selasky { 8270867995cSHans Petter Selasky int error; 8280867995cSHans Petter Selasky 8290867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 8300867995cSHans Petter Selasky UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len); 8310867995cSHans Petter Selasky return (error); 8320867995cSHans Petter Selasky } 8330867995cSHans Petter Selasky 8340867995cSHans Petter Selasky static void 8350867995cSHans Petter Selasky udl_fbmem_alloc(struct udl_softc *sc) 8360867995cSHans Petter Selasky { 8370867995cSHans Petter Selasky uint32_t size; 8380867995cSHans Petter Selasky 8390867995cSHans Petter Selasky size = udl_get_fb_size(sc); 8400867995cSHans Petter Selasky size = round_page(size); 84155e11a03SHans Petter Selasky /* check for zero size */ 84255e11a03SHans Petter Selasky if (size == 0) 84355e11a03SHans Petter Selasky size = PAGE_SIZE; 84461948b25SHans Petter Selasky /* 84561948b25SHans Petter Selasky * It is assumed that allocations above PAGE_SIZE bytes will 84661948b25SHans Petter Selasky * be PAGE_SIZE aligned for use with mmap() 84761948b25SHans Petter Selasky */ 84855e11a03SHans Petter Selasky sc->sc_fb_addr = udl_buffer_alloc(size); 84955e11a03SHans Petter Selasky sc->sc_fb_copy = malloc(size, M_USB_DL, M_WAITOK | M_ZERO); 8500867995cSHans Petter Selasky sc->sc_fb_size = size; 8510867995cSHans Petter Selasky } 8520867995cSHans Petter Selasky 8530867995cSHans Petter Selasky static void 8540867995cSHans Petter Selasky udl_cmd_insert_int_1(struct udl_cmd_buf *cb, uint8_t value) 8550867995cSHans Petter Selasky { 8560867995cSHans Petter Selasky 8570867995cSHans Petter Selasky cb->buf[cb->off] = value; 8580867995cSHans Petter Selasky cb->off += 1; 8590867995cSHans Petter Selasky } 8600867995cSHans Petter Selasky 8610867995cSHans Petter Selasky #if 0 8620867995cSHans Petter Selasky static void 8630867995cSHans Petter Selasky udl_cmd_insert_int_2(struct udl_cmd_buf *cb, uint16_t value) 8640867995cSHans Petter Selasky { 8650867995cSHans Petter Selasky uint16_t lvalue; 8660867995cSHans Petter Selasky 8670867995cSHans Petter Selasky lvalue = htobe16(value); 8680867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 2); 8690867995cSHans Petter Selasky 8700867995cSHans Petter Selasky cb->off += 2; 8710867995cSHans Petter Selasky } 8720867995cSHans Petter Selasky 8730867995cSHans Petter Selasky #endif 8740867995cSHans Petter Selasky 8750867995cSHans Petter Selasky static void 8760867995cSHans Petter Selasky udl_cmd_insert_int_3(struct udl_cmd_buf *cb, uint32_t value) 8770867995cSHans Petter Selasky { 8780867995cSHans Petter Selasky uint32_t lvalue; 8790867995cSHans Petter Selasky 8800867995cSHans Petter Selasky #if BYTE_ORDER == BIG_ENDIAN 8810867995cSHans Petter Selasky lvalue = htobe32(value) << 8; 8820867995cSHans Petter Selasky #else 8830867995cSHans Petter Selasky lvalue = htobe32(value) >> 8; 8840867995cSHans Petter Selasky #endif 8850867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 3); 8860867995cSHans Petter Selasky 8870867995cSHans Petter Selasky cb->off += 3; 8880867995cSHans Petter Selasky } 8890867995cSHans Petter Selasky 8900867995cSHans Petter Selasky #if 0 8910867995cSHans Petter Selasky static void 8920867995cSHans Petter Selasky udl_cmd_insert_int_4(struct udl_cmd_buf *cb, uint32_t value) 8930867995cSHans Petter Selasky { 8940867995cSHans Petter Selasky uint32_t lvalue; 8950867995cSHans Petter Selasky 8960867995cSHans Petter Selasky lvalue = htobe32(value); 8970867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 4); 8980867995cSHans Petter Selasky 8990867995cSHans Petter Selasky cb->off += 4; 9000867995cSHans Petter Selasky } 9010867995cSHans Petter Selasky 9020867995cSHans Petter Selasky #endif 9030867995cSHans Petter Selasky 9040867995cSHans Petter Selasky static void 9050867995cSHans Petter Selasky udl_cmd_insert_buf_le16(struct udl_cmd_buf *cb, const uint8_t *buf, uint32_t len) 9060867995cSHans Petter Selasky { 9070867995cSHans Petter Selasky uint32_t x; 9080867995cSHans Petter Selasky 9090867995cSHans Petter Selasky for (x = 0; x != len; x += 2) { 9100867995cSHans Petter Selasky /* byte swap from little endian to big endian */ 9110867995cSHans Petter Selasky cb->buf[cb->off + x + 0] = buf[x + 1]; 9120867995cSHans Petter Selasky cb->buf[cb->off + x + 1] = buf[x + 0]; 9130867995cSHans Petter Selasky } 9140867995cSHans Petter Selasky cb->off += len; 9150867995cSHans Petter Selasky } 9160867995cSHans Petter Selasky 9170867995cSHans Petter Selasky static void 9180867995cSHans Petter Selasky udl_cmd_write_reg_1(struct udl_cmd_buf *cb, uint8_t reg, uint8_t val) 9190867995cSHans Petter Selasky { 9200867995cSHans Petter Selasky 9210867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 9220867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_REG_WRITE_1); 9230867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, reg); 9240867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, val); 9250867995cSHans Petter Selasky } 9260867995cSHans Petter Selasky 9270867995cSHans Petter Selasky static void 9280867995cSHans Petter Selasky udl_cmd_write_reg_3(struct udl_cmd_buf *cb, uint8_t reg, uint32_t val) 9290867995cSHans Petter Selasky { 9300867995cSHans Petter Selasky 9310867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 0, (val >> 16) & 0xff); 9320867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 1, (val >> 8) & 0xff); 9330867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 2, (val >> 0) & 0xff); 9340867995cSHans Petter Selasky } 9350867995cSHans Petter Selasky 9360867995cSHans Petter Selasky static int 9370867995cSHans Petter Selasky udl_init_chip(struct udl_softc *sc) 9380867995cSHans Petter Selasky { 9390867995cSHans Petter Selasky uint32_t ui32; 9400867995cSHans Petter Selasky uint8_t ui8; 9410867995cSHans Petter Selasky int error; 9420867995cSHans Petter Selasky 9430867995cSHans Petter Selasky error = udl_poll(sc, &ui32); 9440867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9450867995cSHans Petter Selasky return (error); 9460867995cSHans Petter Selasky DPRINTF("poll=0x%08x\n", ui32); 9470867995cSHans Petter Selasky 9480867995cSHans Petter Selasky /* Some products may use later chip too */ 9490867995cSHans Petter Selasky switch (ui32 & 0xff) { 9500867995cSHans Petter Selasky case 0xf1: /* DL1x5 */ 9510867995cSHans Petter Selasky switch (sc->sc_chip) { 9520867995cSHans Petter Selasky case DL120: 9530867995cSHans Petter Selasky sc->sc_chip = DL125; 9540867995cSHans Petter Selasky break; 9550867995cSHans Petter Selasky case DL160: 9560867995cSHans Petter Selasky sc->sc_chip = DL165; 9570867995cSHans Petter Selasky break; 9580867995cSHans Petter Selasky } 9590867995cSHans Petter Selasky break; 9600867995cSHans Petter Selasky } 9610867995cSHans Petter Selasky DPRINTF("chip 0x%04x\n", sc->sc_chip); 9620867995cSHans Petter Selasky 9630867995cSHans Petter Selasky error = udl_read_1(sc, 0xc484, &ui8); 9640867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9650867995cSHans Petter Selasky return (error); 9660867995cSHans Petter Selasky DPRINTF("read 0x%02x from 0xc484\n", ui8); 9670867995cSHans Petter Selasky 9680867995cSHans Petter Selasky error = udl_write_1(sc, 0xc41f, 0x01); 9690867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9700867995cSHans Petter Selasky return (error); 9710867995cSHans Petter Selasky DPRINTF("write 0x01 to 0xc41f\n"); 9720867995cSHans Petter Selasky 9730867995cSHans Petter Selasky error = udl_read_edid(sc, sc->sc_edid); 9740867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9750867995cSHans Petter Selasky return (error); 9760867995cSHans Petter Selasky DPRINTF("read EDID\n"); 9770867995cSHans Petter Selasky 9780867995cSHans Petter Selasky error = udl_set_enc_key(sc, __DECONST(void *, udl_null_key_1), 9790867995cSHans Petter Selasky sizeof(udl_null_key_1)); 9800867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9810867995cSHans Petter Selasky return (error); 9820867995cSHans Petter Selasky DPRINTF("set encryption key\n"); 9830867995cSHans Petter Selasky 9840867995cSHans Petter Selasky error = udl_write_1(sc, 0xc40b, 0x00); 9850867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9860867995cSHans Petter Selasky return (error); 9870867995cSHans Petter Selasky DPRINTF("write 0x00 to 0xc40b\n"); 9880867995cSHans Petter Selasky 9890867995cSHans Petter Selasky return (USB_ERR_NORMAL_COMPLETION); 9900867995cSHans Petter Selasky } 9910867995cSHans Petter Selasky 9920867995cSHans Petter Selasky static void 9930867995cSHans Petter Selasky udl_init_fb_offsets(struct udl_cmd_buf *cb, uint32_t start16, uint32_t stride16, 9940867995cSHans Petter Selasky uint32_t start8, uint32_t stride8) 9950867995cSHans Petter Selasky { 9960867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 9970867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START16, start16); 9980867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE16, stride16); 9990867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START8, start8); 10000867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE8, stride8); 10010867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 10020867995cSHans Petter Selasky } 10030867995cSHans Petter Selasky 10040867995cSHans Petter Selasky static int 10050867995cSHans Petter Selasky udl_init_resolution(struct udl_softc *sc) 10060867995cSHans Petter Selasky { 10070867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 10080867995cSHans Petter Selasky const uint8_t *buf = udl_modes[sc->sc_cur_mode].mode; 10090867995cSHans Petter Selasky struct udl_cmd_buf *cb; 10100867995cSHans Petter Selasky uint32_t delta; 10110867995cSHans Petter Selasky uint32_t i; 10120867995cSHans Petter Selasky int error; 10130867995cSHans Petter Selasky 10140867995cSHans Petter Selasky /* get new buffer */ 10150867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 10160867995cSHans Petter Selasky if (cb == NULL) 10170867995cSHans Petter Selasky return (EAGAIN); 10180867995cSHans Petter Selasky 10190867995cSHans Petter Selasky /* write resolution values and set video memory offsets */ 10200867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 10210867995cSHans Petter Selasky for (i = 0; i < UDL_MODE_SIZE; i++) 10220867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, i, buf[i]); 10230867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 10240867995cSHans Petter Selasky 10250867995cSHans Petter Selasky udl_init_fb_offsets(cb, 0x000000, 0x000a00, 0x555555, 0x000500); 10260867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 10270867995cSHans Petter Selasky 10280867995cSHans Petter Selasky /* fill screen with black color */ 10290867995cSHans Petter Selasky for (i = 0; i < max; i += delta) { 10300867995cSHans Petter Selasky static const uint8_t udl_black[UDL_CMD_MAX_PIXEL_COUNT * 2] __aligned(4); 10310867995cSHans Petter Selasky 10320867995cSHans Petter Selasky delta = max - i; 10330867995cSHans Petter Selasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 10340867995cSHans Petter Selasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 10350867995cSHans Petter Selasky if (i == 0) 10360867995cSHans Petter Selasky error = udl_cmd_write_buf_le16(sc, udl_black, i, delta / 2, M_WAITOK); 10370867995cSHans Petter Selasky else 10380867995cSHans Petter Selasky error = udl_cmd_buf_copy_le16(sc, 0, i, delta / 2, M_WAITOK); 10390867995cSHans Petter Selasky if (error) 10400867995cSHans Petter Selasky return (error); 10410867995cSHans Petter Selasky } 10420867995cSHans Petter Selasky 10430867995cSHans Petter Selasky /* get new buffer */ 10440867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 10450867995cSHans Petter Selasky if (cb == NULL) 10460867995cSHans Petter Selasky return (EAGAIN); 10470867995cSHans Petter Selasky 10480867995cSHans Petter Selasky /* show framebuffer content */ 10490867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 10500867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 10510867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 10520867995cSHans Petter Selasky return (0); 10530867995cSHans Petter Selasky } 10540867995cSHans Petter Selasky 10550867995cSHans Petter Selasky static void 10560867995cSHans Petter Selasky udl_select_mode(struct udl_softc *sc) 10570867995cSHans Petter Selasky { 10580867995cSHans Petter Selasky struct udl_mode mode; 10590867995cSHans Petter Selasky int index = UDL_MAX_MODES; 10600867995cSHans Petter Selasky int i; 10610867995cSHans Petter Selasky 10620867995cSHans Petter Selasky /* try to get the preferred mode from EDID */ 10630867995cSHans Petter Selasky edid_parse(sc->sc_edid, &sc->sc_edid_info); 10640867995cSHans Petter Selasky #ifdef USB_DEBUG 10650867995cSHans Petter Selasky edid_print(&sc->sc_edid_info); 10660867995cSHans Petter Selasky #endif 10670867995cSHans Petter Selasky if (sc->sc_edid_info.edid_preferred_mode != NULL) { 10680867995cSHans Petter Selasky mode.hz = 10690867995cSHans Petter Selasky (sc->sc_edid_info.edid_preferred_mode->dot_clock * 1000) / 10700867995cSHans Petter Selasky (sc->sc_edid_info.edid_preferred_mode->htotal * 10710867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->vtotal); 10720867995cSHans Petter Selasky mode.clock = 10730867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->dot_clock / 10; 10740867995cSHans Petter Selasky mode.hdisplay = 10750867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->hdisplay; 10760867995cSHans Petter Selasky mode.vdisplay = 10770867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->vdisplay; 10780867995cSHans Petter Selasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, mode.hz, 10790867995cSHans Petter Selasky sc->sc_chip, mode.clock); 10800867995cSHans Petter Selasky sc->sc_cur_mode = index; 10810867995cSHans Petter Selasky } else { 10820867995cSHans Petter Selasky DPRINTF("no preferred mode found!\n"); 10830867995cSHans Petter Selasky } 10840867995cSHans Petter Selasky 10850867995cSHans Petter Selasky if (index == UDL_MAX_MODES) { 108630b44000SHans Petter Selasky DPRINTF("no mode line found\n"); 10870867995cSHans Petter Selasky 10880867995cSHans Petter Selasky i = 0; 10890867995cSHans Petter Selasky while (i < sc->sc_edid_info.edid_nmodes) { 10900867995cSHans Petter Selasky mode.hz = 10910867995cSHans Petter Selasky (sc->sc_edid_info.edid_modes[i].dot_clock * 1000) / 10920867995cSHans Petter Selasky (sc->sc_edid_info.edid_modes[i].htotal * 10930867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].vtotal); 10940867995cSHans Petter Selasky mode.clock = 10950867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].dot_clock / 10; 10960867995cSHans Petter Selasky mode.hdisplay = 10970867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].hdisplay; 10980867995cSHans Petter Selasky mode.vdisplay = 10990867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].vdisplay; 11000867995cSHans Petter Selasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, 11010867995cSHans Petter Selasky mode.hz, sc->sc_chip, mode.clock); 11020867995cSHans Petter Selasky if (index < UDL_MAX_MODES) 11030867995cSHans Petter Selasky if ((sc->sc_cur_mode == UDL_MAX_MODES) || 11040867995cSHans Petter Selasky (index > sc->sc_cur_mode)) 11050867995cSHans Petter Selasky sc->sc_cur_mode = index; 11060867995cSHans Petter Selasky i++; 11070867995cSHans Petter Selasky } 11080867995cSHans Petter Selasky } 11090867995cSHans Petter Selasky /* 11100867995cSHans Petter Selasky * If no mode found use default. 11110867995cSHans Petter Selasky */ 11120867995cSHans Petter Selasky if (sc->sc_cur_mode == UDL_MAX_MODES) 11130867995cSHans Petter Selasky sc->sc_cur_mode = udl_lookup_mode(800, 600, 60, sc->sc_chip, 0); 11140867995cSHans Petter Selasky } 11150867995cSHans Petter Selasky 11160867995cSHans Petter Selasky static int 11170867995cSHans Petter Selasky udl_cmd_write_buf_le16(struct udl_softc *sc, const uint8_t *buf, uint32_t off, 11180867995cSHans Petter Selasky uint8_t pixels, int flags) 11190867995cSHans Petter Selasky { 11200867995cSHans Petter Selasky struct udl_cmd_buf *cb; 11210867995cSHans Petter Selasky 11220867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 11230867995cSHans Petter Selasky if (cb == NULL) 11240867995cSHans Petter Selasky return (EAGAIN); 11250867995cSHans Petter Selasky 11260867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 11270867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 11280867995cSHans Petter Selasky udl_cmd_insert_int_3(cb, off); 11290867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, pixels); 11300867995cSHans Petter Selasky udl_cmd_insert_buf_le16(cb, buf, 2 * pixels); 11310867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 11320867995cSHans Petter Selasky 11330867995cSHans Petter Selasky return (0); 11340867995cSHans Petter Selasky } 11350867995cSHans Petter Selasky 11360867995cSHans Petter Selasky static int 11370867995cSHans Petter Selasky udl_cmd_buf_copy_le16(struct udl_softc *sc, uint32_t src, uint32_t dst, 11380867995cSHans Petter Selasky uint8_t pixels, int flags) 11390867995cSHans Petter Selasky { 11400867995cSHans Petter Selasky struct udl_cmd_buf *cb; 11410867995cSHans Petter Selasky 11420867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 11430867995cSHans Petter Selasky if (cb == NULL) 11440867995cSHans Petter Selasky return (EAGAIN); 11450867995cSHans Petter Selasky 11460867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 11470867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD); 1148b161d698SHans Petter Selasky udl_cmd_insert_int_3(cb, dst); 11490867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, pixels); 1150b161d698SHans Petter Selasky udl_cmd_insert_int_3(cb, src); 11510867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 11520867995cSHans Petter Selasky 11530867995cSHans Petter Selasky return (0); 11540867995cSHans Petter Selasky } 1155