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 630867995cSHans Petter Selasky #ifdef USB_DEBUG 640867995cSHans Petter Selasky static int udl_debug = 0; 650867995cSHans Petter Selasky 660867995cSHans Petter Selasky static SYSCTL_NODE(_hw_usb, OID_AUTO, udl, CTLFLAG_RW, 0, "USB UDL"); 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 720867995cSHans Petter Selasky /* 730867995cSHans Petter Selasky * Prototypes. 740867995cSHans Petter Selasky */ 750867995cSHans Petter Selasky static usb_callback_t udl_bulk_write_callback; 760867995cSHans Petter Selasky 770867995cSHans Petter Selasky static device_probe_t udl_probe; 780867995cSHans Petter Selasky static device_attach_t udl_attach; 790867995cSHans Petter Selasky static device_detach_t udl_detach; 800867995cSHans Petter Selasky static fb_getinfo_t udl_fb_getinfo; 810867995cSHans Petter Selasky #if 0 820867995cSHans Petter Selasky static fb_blank_display_t udl_fb_blank_display; 830867995cSHans Petter Selasky #endif 840867995cSHans Petter Selasky 850867995cSHans Petter Selasky static void udl_select_chip(struct udl_softc *, struct usb_attach_arg *); 860867995cSHans Petter Selasky static int udl_init_chip(struct udl_softc *); 870867995cSHans Petter Selasky static void udl_select_mode(struct udl_softc *); 880867995cSHans Petter Selasky static int udl_init_resolution(struct udl_softc *); 890867995cSHans Petter Selasky static void udl_fbmem_alloc(struct udl_softc *); 900867995cSHans Petter Selasky static int udl_cmd_write_buf_le16(struct udl_softc *, const uint8_t *, uint32_t, uint8_t, int); 910867995cSHans Petter Selasky static int udl_cmd_buf_copy_le16(struct udl_softc *, uint32_t, uint32_t, uint8_t, int); 920867995cSHans Petter Selasky static void udl_cmd_insert_int_1(struct udl_cmd_buf *, uint8_t); 930867995cSHans Petter Selasky static void udl_cmd_insert_int_3(struct udl_cmd_buf *, uint32_t); 940867995cSHans Petter Selasky static void udl_cmd_insert_buf_le16(struct udl_cmd_buf *, const uint8_t *, uint32_t); 950867995cSHans Petter Selasky static void udl_cmd_write_reg_1(struct udl_cmd_buf *, uint8_t, uint8_t); 960867995cSHans Petter Selasky static void udl_cmd_write_reg_3(struct udl_cmd_buf *, uint8_t, uint32_t); 970867995cSHans Petter Selasky #if 0 980867995cSHans Petter Selasky static int udl_power_save(struct udl_softc *, int, int); 990867995cSHans Petter Selasky #endif 1000867995cSHans Petter Selasky 1010867995cSHans Petter Selasky static const struct usb_config udl_config[UDL_N_TRANSFER] = { 1020867995cSHans Petter Selasky [UDL_BULK_WRITE_0] = { 1030867995cSHans Petter Selasky .type = UE_BULK, 1040867995cSHans Petter Selasky .endpoint = UE_ADDR_ANY, 1050867995cSHans Petter Selasky .direction = UE_DIR_TX, 1060867995cSHans Petter Selasky .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, 1070867995cSHans Petter Selasky .bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES, 1080867995cSHans Petter Selasky .callback = &udl_bulk_write_callback, 1090867995cSHans Petter Selasky .frames = UDL_CMD_MAX_FRAMES, 1100867995cSHans Petter Selasky .timeout = 5000, /* 5 seconds */ 1110867995cSHans Petter Selasky }, 1120867995cSHans Petter Selasky [UDL_BULK_WRITE_1] = { 1130867995cSHans Petter Selasky .type = UE_BULK, 1140867995cSHans Petter Selasky .endpoint = UE_ADDR_ANY, 1150867995cSHans Petter Selasky .direction = UE_DIR_TX, 1160867995cSHans Petter Selasky .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,}, 1170867995cSHans Petter Selasky .bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES, 1180867995cSHans Petter Selasky .callback = &udl_bulk_write_callback, 1190867995cSHans Petter Selasky .frames = UDL_CMD_MAX_FRAMES, 1200867995cSHans Petter Selasky .timeout = 5000, /* 5 seconds */ 1210867995cSHans Petter Selasky }, 1220867995cSHans Petter Selasky }; 1230867995cSHans Petter Selasky 1240867995cSHans Petter Selasky /* 1250867995cSHans Petter Selasky * Driver glue. 1260867995cSHans Petter Selasky */ 1270867995cSHans Petter Selasky static devclass_t udl_devclass; 1280867995cSHans Petter Selasky 1290867995cSHans Petter Selasky static device_method_t udl_methods[] = { 1300867995cSHans Petter Selasky DEVMETHOD(device_probe, udl_probe), 1310867995cSHans Petter Selasky DEVMETHOD(device_attach, udl_attach), 1320867995cSHans Petter Selasky DEVMETHOD(device_detach, udl_detach), 1330867995cSHans Petter Selasky DEVMETHOD(fb_getinfo, udl_fb_getinfo), 1340867995cSHans Petter Selasky #if 0 1350867995cSHans Petter Selasky DEVMETHOD(fb_blank_display, udl_fb_blank_display), 1360867995cSHans Petter Selasky #endif 1370867995cSHans Petter Selasky DEVMETHOD_END 1380867995cSHans Petter Selasky }; 1390867995cSHans Petter Selasky 1400867995cSHans Petter Selasky static driver_t udl_driver = { 1410867995cSHans Petter Selasky .name = "udl", 1420867995cSHans Petter Selasky .methods = udl_methods, 1430867995cSHans Petter Selasky .size = sizeof(struct udl_softc), 1440867995cSHans Petter Selasky }; 1450867995cSHans Petter Selasky 1460867995cSHans Petter Selasky DRIVER_MODULE(udl, uhub, udl_driver, udl_devclass, NULL, NULL); 1470867995cSHans Petter Selasky MODULE_DEPEND(udl, usb, 1, 1, 1); 1480867995cSHans Petter Selasky MODULE_DEPEND(udl, fbd, 1, 1, 1); 1490867995cSHans Petter Selasky MODULE_DEPEND(udl, videomode, 1, 1, 1); 1500867995cSHans Petter Selasky MODULE_VERSION(udl, 1); 1510867995cSHans Petter Selasky 1520867995cSHans Petter Selasky /* 1530867995cSHans Petter Selasky * Matching devices. 1540867995cSHans Petter Selasky */ 1550867995cSHans Petter Selasky static const STRUCT_USB_HOST_ID udl_devs[] = { 1560867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U, DL120)}, 1570867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U, DL120)}, 1580867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020, DL160)}, 1590867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220, DL165)}, 1600867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60, DL160)}, 1610867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI, DL160)}, 1620867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10, DL120)}, 1630867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI, DLUNK)}, 1640867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008, DL160)}, 1650867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK, DL160)}, 1660867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571, DL160)}, 1670867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061, DL195)}, 1680867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK, DL165)}, 1690867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI, DLUNK)}, 1700867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0, DL120)}, 1710867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV, DL160)}, 1720867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70, DL125)}, 1730867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2, DLUNK)}, 1740867995cSHans Petter Selasky {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)} 1750867995cSHans Petter Selasky }; 1760867995cSHans Petter Selasky 1770867995cSHans Petter Selasky static uint32_t 1780867995cSHans Petter Selasky udl_get_fb_size(struct udl_softc *sc) 1790867995cSHans Petter Selasky { 1800867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 1810867995cSHans Petter Selasky 1820867995cSHans Petter Selasky return ((uint32_t)udl_modes[i].hdisplay * 1830867995cSHans Petter Selasky (uint32_t)udl_modes[i].vdisplay * 2); 1840867995cSHans Petter Selasky } 1850867995cSHans Petter Selasky 1860867995cSHans Petter Selasky static uint32_t 1870867995cSHans Petter Selasky udl_get_fb_width(struct udl_softc *sc) 1880867995cSHans Petter Selasky { 1890867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 1900867995cSHans Petter Selasky 1910867995cSHans Petter Selasky return (udl_modes[i].vdisplay); 1920867995cSHans Petter Selasky } 1930867995cSHans Petter Selasky 1940867995cSHans Petter Selasky static uint32_t 1950867995cSHans Petter Selasky udl_get_fb_height(struct udl_softc *sc) 1960867995cSHans Petter Selasky { 1970867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 1980867995cSHans Petter Selasky 1990867995cSHans Petter Selasky return (udl_modes[i].hdisplay); 2000867995cSHans Petter Selasky } 2010867995cSHans Petter Selasky 2020867995cSHans Petter Selasky static uint32_t 2030867995cSHans Petter Selasky udl_get_fb_hz(struct udl_softc *sc) 2040867995cSHans Petter Selasky { 2050867995cSHans Petter Selasky unsigned i = sc->sc_cur_mode; 2060867995cSHans Petter Selasky 2070867995cSHans Petter Selasky return (udl_modes[i].hz); 2080867995cSHans Petter Selasky } 2090867995cSHans Petter Selasky 2100867995cSHans Petter Selasky static void 2110867995cSHans Petter Selasky udl_callout(void *arg) 2120867995cSHans Petter Selasky { 2130867995cSHans Petter Selasky struct udl_softc *sc = arg; 2140867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 2150867995cSHans Petter Selasky 2160867995cSHans Petter Selasky if (sc->sc_power_save == 0) { 2170867995cSHans Petter Selasky if (sc->sc_sync_off >= max) 2180867995cSHans Petter Selasky sc->sc_sync_off = 0; 2190867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 2200867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 2210867995cSHans Petter Selasky } 2220867995cSHans Petter Selasky callout_reset(&sc->sc_callout, hz / 5, &udl_callout, sc); 2230867995cSHans Petter Selasky } 2240867995cSHans Petter Selasky 2250867995cSHans Petter Selasky static int 2260867995cSHans Petter Selasky udl_probe(device_t dev) 2270867995cSHans Petter Selasky { 2280867995cSHans Petter Selasky struct usb_attach_arg *uaa = device_get_ivars(dev); 2290867995cSHans Petter Selasky 2300867995cSHans Petter Selasky if (uaa->usb_mode != USB_MODE_HOST) 2310867995cSHans Petter Selasky return (ENXIO); 2320867995cSHans Petter Selasky if (uaa->info.bConfigIndex != 0) 2330867995cSHans Petter Selasky return (ENXIO); 2340867995cSHans Petter Selasky if (uaa->info.bIfaceIndex != 0) 2350867995cSHans Petter Selasky return (ENXIO); 2360867995cSHans Petter Selasky 2370867995cSHans Petter Selasky return (usbd_lookup_id_by_uaa(udl_devs, sizeof(udl_devs), uaa)); 2380867995cSHans Petter Selasky } 2390867995cSHans Petter Selasky 2400867995cSHans Petter Selasky static int 2410867995cSHans Petter Selasky udl_attach(device_t dev) 2420867995cSHans Petter Selasky { 2430867995cSHans Petter Selasky struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 2440867995cSHans Petter Selasky struct sysctl_oid *tree = device_get_sysctl_tree(dev); 2450867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 2460867995cSHans Petter Selasky struct usb_attach_arg *uaa = device_get_ivars(dev); 2470867995cSHans Petter Selasky int error; 2480867995cSHans Petter Selasky int i; 2490867995cSHans Petter Selasky 2500867995cSHans Petter Selasky device_set_usb_desc(dev); 2510867995cSHans Petter Selasky 2520867995cSHans Petter Selasky mtx_init(&sc->sc_mtx, "UDL lock", NULL, MTX_DEF); 2530867995cSHans Petter Selasky cv_init(&sc->sc_cv, "UDLCV"); 2540867995cSHans Petter Selasky callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 2550867995cSHans Petter Selasky sc->sc_udev = uaa->device; 2560867995cSHans Petter Selasky 2570867995cSHans Petter Selasky error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 2580867995cSHans Petter Selasky sc->sc_xfer, udl_config, UDL_N_TRANSFER, sc, &sc->sc_mtx); 2590867995cSHans Petter Selasky 2600867995cSHans Petter Selasky if (error) { 2610867995cSHans Petter Selasky DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error)); 2620867995cSHans Petter Selasky goto detach; 2630867995cSHans Petter Selasky } 2640867995cSHans Petter Selasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_0], &sc->sc_xfer_head[0]); 2650867995cSHans Petter Selasky usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_1], &sc->sc_xfer_head[1]); 2660867995cSHans Petter Selasky 2670867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_xfer_head[0]); 2680867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_xfer_head[1]); 2690867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_cmd_buf_free); 2700867995cSHans Petter Selasky TAILQ_INIT(&sc->sc_cmd_buf_pending); 2710867995cSHans Petter Selasky 2720867995cSHans Petter Selasky sc->sc_def_chip = -1; 2730867995cSHans Petter Selasky sc->sc_chip = USB_GET_DRIVER_INFO(uaa); 2740867995cSHans Petter Selasky sc->sc_def_mode = -1; 2750867995cSHans Petter Selasky sc->sc_cur_mode = UDL_MAX_MODES; 2760867995cSHans Petter Selasky 2770867995cSHans Petter Selasky /* Allow chip ID to be overwritten */ 2780867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid_force", 2790867995cSHans Petter Selasky CTLFLAG_RWTUN, &sc->sc_def_chip, 0, "chip ID"); 2800867995cSHans Petter Selasky 2810867995cSHans Petter Selasky /* Export current chip ID */ 2820867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid", 2830867995cSHans Petter Selasky CTLFLAG_RD, &sc->sc_chip, 0, "chip ID"); 2840867995cSHans Petter Selasky 2850867995cSHans Petter Selasky if (sc->sc_def_chip > -1 && sc->sc_def_chip <= DLMAX) { 2860867995cSHans Petter Selasky device_printf(dev, "Forcing chip ID to 0x%04x\n", sc->sc_def_chip); 2870867995cSHans Petter Selasky sc->sc_chip = sc->sc_def_chip; 2880867995cSHans Petter Selasky } 2890867995cSHans Petter Selasky /* 2900867995cSHans Petter Selasky * The product might have more than one chip 2910867995cSHans Petter Selasky */ 2920867995cSHans Petter Selasky if (sc->sc_chip == DLUNK) 2930867995cSHans Petter Selasky udl_select_chip(sc, uaa); 2940867995cSHans Petter Selasky 2950867995cSHans Petter Selasky for (i = 0; i != UDL_CMD_MAX_BUFFERS; i++) { 2960867995cSHans Petter Selasky struct udl_cmd_buf *cb = &sc->sc_cmd_buf_temp[i]; 2970867995cSHans Petter Selasky 2980867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 2990867995cSHans Petter Selasky } 3000867995cSHans Petter Selasky 3010867995cSHans Petter Selasky /* 3020867995cSHans Petter Selasky * Initialize chip. 3030867995cSHans Petter Selasky */ 3040867995cSHans Petter Selasky error = udl_init_chip(sc); 3050867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 3060867995cSHans Petter Selasky goto detach; 3070867995cSHans Petter Selasky 3080867995cSHans Petter Selasky /* 3090867995cSHans Petter Selasky * Select edid mode. 3100867995cSHans Petter Selasky */ 3110867995cSHans Petter Selasky udl_select_mode(sc); 3120867995cSHans Petter Selasky 3130867995cSHans Petter Selasky /* Allow default mode to be overwritten */ 3140867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode_force", 3150867995cSHans Petter Selasky CTLFLAG_RWTUN, &sc->sc_def_mode, 0, "mode"); 3160867995cSHans Petter Selasky 3170867995cSHans Petter Selasky /* Export current mode */ 3180867995cSHans Petter Selasky SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode", 3190867995cSHans Petter Selasky CTLFLAG_RD, &sc->sc_cur_mode, 0, "mode"); 3200867995cSHans Petter Selasky 3210867995cSHans Petter Selasky i = sc->sc_def_mode; 3220867995cSHans Petter Selasky if (i > -1 && i < UDL_MAX_MODES) { 3230867995cSHans Petter Selasky if (udl_modes[i].chip <= sc->sc_chip) { 3240867995cSHans Petter Selasky device_printf(dev, "Forcing mode to %d\n", i); 3250867995cSHans Petter Selasky sc->sc_cur_mode = i; 3260867995cSHans Petter Selasky } 3270867995cSHans Petter Selasky } 3280867995cSHans Petter Selasky /* Printout current mode */ 3290867995cSHans Petter Selasky device_printf(dev, "Mode selected %dx%d @ %dHz\n", 3300867995cSHans Petter Selasky (int)udl_get_fb_width(sc), 3310867995cSHans Petter Selasky (int)udl_get_fb_height(sc), 3320867995cSHans Petter Selasky (int)udl_get_fb_hz(sc)); 3330867995cSHans Petter Selasky 3340867995cSHans Petter Selasky udl_init_resolution(sc); 3350867995cSHans Petter Selasky 3360867995cSHans Petter Selasky /* Allocate frame buffer */ 3370867995cSHans Petter Selasky udl_fbmem_alloc(sc); 3380867995cSHans Petter Selasky 3390867995cSHans Petter Selasky UDL_LOCK(sc); 3400867995cSHans Petter Selasky udl_callout(sc); 3410867995cSHans Petter Selasky UDL_UNLOCK(sc); 3420867995cSHans Petter Selasky 3430867995cSHans Petter Selasky sc->sc_fb_info.fb_name = device_get_nameunit(dev); 3440867995cSHans Petter Selasky sc->sc_fb_info.fb_size = sc->sc_fb_size; 3450867995cSHans Petter Selasky sc->sc_fb_info.fb_bpp = 16; 3460867995cSHans Petter Selasky sc->sc_fb_info.fb_depth = 16; 3470867995cSHans Petter Selasky sc->sc_fb_info.fb_width = udl_get_fb_width(sc); 3480867995cSHans Petter Selasky sc->sc_fb_info.fb_height = udl_get_fb_height(sc); 3490867995cSHans Petter Selasky sc->sc_fb_info.fb_stride = sc->sc_fb_info.fb_width * 2; 350*b161d698SHans Petter Selasky sc->sc_fb_info.fb_pbase = 0; 3510867995cSHans Petter Selasky sc->sc_fb_info.fb_vbase = (uintptr_t)sc->sc_fb_addr; 3520867995cSHans Petter Selasky 3530867995cSHans Petter Selasky sc->sc_fbdev = device_add_child(dev, "fbd", -1); 3540867995cSHans Petter Selasky if (sc->sc_fbdev == NULL) 3550867995cSHans Petter Selasky goto detach; 3560867995cSHans Petter Selasky if (device_probe_and_attach(sc->sc_fbdev) != 0) 3570867995cSHans Petter Selasky goto detach; 3580867995cSHans Petter Selasky 3590867995cSHans Petter Selasky return (0); 3600867995cSHans Petter Selasky 3610867995cSHans Petter Selasky detach: 3620867995cSHans Petter Selasky udl_detach(dev); 3630867995cSHans Petter Selasky 3640867995cSHans Petter Selasky return (ENXIO); 3650867995cSHans Petter Selasky } 3660867995cSHans Petter Selasky 3670867995cSHans Petter Selasky static int 3680867995cSHans Petter Selasky udl_detach(device_t dev) 3690867995cSHans Petter Selasky { 3700867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 3710867995cSHans Petter Selasky 3720867995cSHans Petter Selasky if (sc->sc_fbdev != NULL) { 3730867995cSHans Petter Selasky device_t bdev; 3740867995cSHans Petter Selasky 3750867995cSHans Petter Selasky bdev = sc->sc_fbdev; 3760867995cSHans Petter Selasky sc->sc_fbdev = NULL; 3770867995cSHans Petter Selasky device_detach(bdev); 3780867995cSHans Petter Selasky device_delete_child(dev, bdev); 3790867995cSHans Petter Selasky } 3800867995cSHans Petter Selasky UDL_LOCK(sc); 3810867995cSHans Petter Selasky sc->sc_gone = 1; 3820867995cSHans Petter Selasky callout_stop(&sc->sc_callout); 3830867995cSHans Petter Selasky UDL_UNLOCK(sc); 3840867995cSHans Petter Selasky 3850867995cSHans Petter Selasky usbd_transfer_unsetup(sc->sc_xfer, UDL_N_TRANSFER); 3860867995cSHans Petter Selasky 3870867995cSHans Petter Selasky callout_drain(&sc->sc_callout); 3880867995cSHans Petter Selasky 3890867995cSHans Petter Selasky mtx_destroy(&sc->sc_mtx); 3900867995cSHans Petter Selasky cv_destroy(&sc->sc_cv); 3910867995cSHans Petter Selasky 3920867995cSHans Petter Selasky /* 3930867995cSHans Petter Selasky * Free framebuffer memory, if any. 3940867995cSHans Petter Selasky */ 3950867995cSHans Petter Selasky free(sc->sc_fb_addr, M_DEVBUF); 3960867995cSHans Petter Selasky free(sc->sc_fb_copy, M_DEVBUF); 3970867995cSHans Petter Selasky 3980867995cSHans Petter Selasky return (0); 3990867995cSHans Petter Selasky } 4000867995cSHans Petter Selasky 4010867995cSHans Petter Selasky static struct fb_info * 4020867995cSHans Petter Selasky udl_fb_getinfo(device_t dev) 4030867995cSHans Petter Selasky { 4040867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 4050867995cSHans Petter Selasky 4060867995cSHans Petter Selasky return (&sc->sc_fb_info); 4070867995cSHans Petter Selasky } 4080867995cSHans Petter Selasky 4090867995cSHans Petter Selasky #if 0 4100867995cSHans Petter Selasky static int 4110867995cSHans Petter Selasky udl_fb_blank_display(device_t dev, int mode) 4120867995cSHans Petter Selasky { 4130867995cSHans Petter Selasky struct udl_softc *sc = device_get_softc(dev); 4140867995cSHans Petter Selasky 4150867995cSHans Petter Selasky switch (mode) { 4160867995cSHans Petter Selasky case V_DISPLAY_ON: 4170867995cSHans Petter Selasky udl_power_save(sc, 1, M_WAITOK); 4180867995cSHans Petter Selasky break; 4190867995cSHans Petter Selasky case V_DISPLAY_BLANK: 4200867995cSHans Petter Selasky udl_power_save(sc, 1, M_WAITOK); 4210867995cSHans Petter Selasky if (sc->sc_fb_addr != 0) { 4220867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 4230867995cSHans Petter Selasky 4240867995cSHans Petter Selasky memset((void *)sc->sc_fb_addr, 0, max); 4250867995cSHans Petter Selasky } 4260867995cSHans Petter Selasky break; 4270867995cSHans Petter Selasky case V_DISPLAY_STAND_BY: 4280867995cSHans Petter Selasky case V_DISPLAY_SUSPEND: 4290867995cSHans Petter Selasky udl_power_save(sc, 0, M_WAITOK); 4300867995cSHans Petter Selasky break; 4310867995cSHans Petter Selasky } 4320867995cSHans Petter Selasky return (0); 4330867995cSHans Petter Selasky } 4340867995cSHans Petter Selasky #endif 4350867995cSHans Petter Selasky 4360867995cSHans Petter Selasky static struct udl_cmd_buf * 4370867995cSHans Petter Selasky udl_cmd_buf_alloc(struct udl_softc *sc, int flags) 4380867995cSHans Petter Selasky { 4390867995cSHans Petter Selasky struct udl_cmd_buf *cb; 4400867995cSHans Petter Selasky 4410867995cSHans Petter Selasky UDL_LOCK(sc); 4420867995cSHans Petter Selasky while ((cb = TAILQ_FIRST(&sc->sc_cmd_buf_free)) == NULL) { 4430867995cSHans Petter Selasky if (flags != M_WAITOK) 4440867995cSHans Petter Selasky break; 4450867995cSHans Petter Selasky cv_wait(&sc->sc_cv, &sc->sc_mtx); 4460867995cSHans Petter Selasky } 4470867995cSHans Petter Selasky if (cb != NULL) { 4480867995cSHans Petter Selasky TAILQ_REMOVE(&sc->sc_cmd_buf_free, cb, entry); 4490867995cSHans Petter Selasky cb->off = 0; 4500867995cSHans Petter Selasky } 4510867995cSHans Petter Selasky UDL_UNLOCK(sc); 4520867995cSHans Petter Selasky return (cb); 4530867995cSHans Petter Selasky } 4540867995cSHans Petter Selasky 4550867995cSHans Petter Selasky static void 4560867995cSHans Petter Selasky udl_cmd_buf_send(struct udl_softc *sc, struct udl_cmd_buf *cb) 4570867995cSHans Petter Selasky { 4580867995cSHans Petter Selasky UDL_LOCK(sc); 4590867995cSHans Petter Selasky if (sc->sc_gone) { 4600867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry); 4610867995cSHans Petter Selasky } else { 4620867995cSHans Petter Selasky /* mark end of command stack */ 4630867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 4640867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_EOC); 4650867995cSHans Petter Selasky 4660867995cSHans Petter Selasky TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_pending, cb, entry); 4670867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]); 4680867995cSHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]); 4690867995cSHans Petter Selasky } 4700867995cSHans Petter Selasky UDL_UNLOCK(sc); 4710867995cSHans Petter Selasky } 4720867995cSHans Petter Selasky 4730867995cSHans Petter Selasky static struct udl_cmd_buf * 4740867995cSHans Petter Selasky udl_fb_synchronize(struct udl_softc *sc) 4750867995cSHans Petter Selasky { 4760867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 4770867995cSHans Petter Selasky 478*b161d698SHans Petter Selasky /* check if framebuffer is not ready */ 479*b161d698SHans Petter Selasky if (sc->sc_fb_addr == NULL || 480*b161d698SHans Petter Selasky sc->sc_fb_copy == NULL) 481*b161d698SHans Petter Selasky return (NULL); 482*b161d698SHans Petter Selasky 4830867995cSHans Petter Selasky while (sc->sc_sync_off < max) { 4840867995cSHans Petter Selasky uint32_t delta = max - sc->sc_sync_off; 4850867995cSHans Petter Selasky 4860867995cSHans Petter Selasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 4870867995cSHans Petter Selasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 4880867995cSHans Petter Selasky if (bcmp(sc->sc_fb_addr + sc->sc_sync_off, sc->sc_fb_copy + sc->sc_sync_off, delta) != 0) { 4890867995cSHans Petter Selasky struct udl_cmd_buf *cb; 4900867995cSHans Petter Selasky 4910867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, M_NOWAIT); 4920867995cSHans Petter Selasky if (cb == NULL) 4930867995cSHans Petter Selasky goto done; 4940867995cSHans Petter Selasky memcpy(sc->sc_fb_copy + sc->sc_sync_off, 4950867995cSHans Petter Selasky sc->sc_fb_addr + sc->sc_sync_off, delta); 4960867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 4970867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 4980867995cSHans Petter Selasky udl_cmd_insert_int_3(cb, sc->sc_sync_off); 4990867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, delta / 2); 5000867995cSHans Petter Selasky udl_cmd_insert_buf_le16(cb, sc->sc_fb_copy + sc->sc_sync_off, delta); 5010867995cSHans Petter Selasky sc->sc_sync_off += delta; 5020867995cSHans Petter Selasky return (cb); 5030867995cSHans Petter Selasky } else { 5040867995cSHans Petter Selasky sc->sc_sync_off += delta; 5050867995cSHans Petter Selasky } 5060867995cSHans Petter Selasky } 5070867995cSHans Petter Selasky done: 5080867995cSHans Petter Selasky return (NULL); 5090867995cSHans Petter Selasky } 5100867995cSHans Petter Selasky 5110867995cSHans Petter Selasky static void 5120867995cSHans Petter Selasky udl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 5130867995cSHans Petter Selasky { 5140867995cSHans Petter Selasky struct udl_softc *sc = usbd_xfer_softc(xfer); 5150867995cSHans Petter Selasky struct udl_cmd_head *phead = usbd_xfer_get_priv(xfer); 5160867995cSHans Petter Selasky struct udl_cmd_buf *cb; 5170867995cSHans Petter Selasky unsigned i; 5180867995cSHans Petter Selasky 5190867995cSHans Petter Selasky switch (USB_GET_STATE(xfer)) { 5200867995cSHans Petter Selasky case USB_ST_TRANSFERRED: 5210867995cSHans Petter Selasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 5220867995cSHans Petter Selasky case USB_ST_SETUP: 5230867995cSHans Petter Selasky tr_setup: 5240867995cSHans Petter Selasky for (i = 0; i != UDL_CMD_MAX_FRAMES; i++) { 5250867995cSHans Petter Selasky cb = TAILQ_FIRST(&sc->sc_cmd_buf_pending); 5260867995cSHans Petter Selasky if (cb == NULL) { 5270867995cSHans Petter Selasky cb = udl_fb_synchronize(sc); 5280867995cSHans Petter Selasky if (cb == NULL) 5290867995cSHans Petter Selasky break; 530*b161d698SHans Petter Selasky } else { 5310867995cSHans Petter Selasky TAILQ_REMOVE(&sc->sc_cmd_buf_pending, cb, entry); 532*b161d698SHans Petter Selasky } 5330867995cSHans Petter Selasky TAILQ_INSERT_TAIL(phead, cb, entry); 5340867995cSHans Petter Selasky usbd_xfer_set_frame_data(xfer, i, cb->buf, cb->off); 5350867995cSHans Petter Selasky } 5360867995cSHans Petter Selasky if (i != 0) { 5370867995cSHans Petter Selasky usbd_xfer_set_frames(xfer, i); 5380867995cSHans Petter Selasky usbd_transfer_submit(xfer); 5390867995cSHans Petter Selasky } 5400867995cSHans Petter Selasky break; 5410867995cSHans Petter Selasky default: 5420867995cSHans Petter Selasky TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry); 5430867995cSHans Petter Selasky if (error != USB_ERR_CANCELLED) { 5440867995cSHans Petter Selasky /* try clear stall first */ 5450867995cSHans Petter Selasky usbd_xfer_set_stall(xfer); 5460867995cSHans Petter Selasky goto tr_setup; 5470867995cSHans Petter Selasky } 5480867995cSHans Petter Selasky break; 5490867995cSHans Petter Selasky } 5500867995cSHans Petter Selasky /* wakeup any waiters */ 5510867995cSHans Petter Selasky cv_signal(&sc->sc_cv); 5520867995cSHans Petter Selasky } 5530867995cSHans Petter Selasky 5540867995cSHans Petter Selasky #if 0 5550867995cSHans Petter Selasky static int 5560867995cSHans Petter Selasky udl_power_save(struct udl_softc *sc, int on, int flags) 5570867995cSHans Petter Selasky { 5580867995cSHans Petter Selasky struct udl_cmd_buf *cb; 5590867995cSHans Petter Selasky 5600867995cSHans Petter Selasky /* get new buffer */ 5610867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 5620867995cSHans Petter Selasky if (cb == NULL) 5630867995cSHans Petter Selasky return (EAGAIN); 5640867995cSHans Petter Selasky 5650867995cSHans Petter Selasky DPRINTF("screen %s\n", on ? "ON" : "OFF"); 5660867995cSHans Petter Selasky 5670867995cSHans Petter Selasky sc->sc_power_save = on ? 0 : 1; 5680867995cSHans Petter Selasky 5690867995cSHans Petter Selasky if (on) 5700867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 5710867995cSHans Petter Selasky else 5720867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF); 5730867995cSHans Petter Selasky 5740867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 5750867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 5760867995cSHans Petter Selasky return (0); 5770867995cSHans Petter Selasky } 5780867995cSHans Petter Selasky #endif 5790867995cSHans Petter Selasky 5800867995cSHans Petter Selasky static int 5810867995cSHans Petter Selasky udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r, 5820867995cSHans Petter Selasky uint16_t index, uint16_t value, uint8_t *buf, size_t len) 5830867995cSHans Petter Selasky { 5840867995cSHans Petter Selasky usb_device_request_t req; 5850867995cSHans Petter Selasky int error; 5860867995cSHans Petter Selasky 5870867995cSHans Petter Selasky req.bmRequestType = rt; 5880867995cSHans Petter Selasky req.bRequest = r; 5890867995cSHans Petter Selasky USETW(req.wIndex, index); 5900867995cSHans Petter Selasky USETW(req.wValue, value); 5910867995cSHans Petter Selasky USETW(req.wLength, len); 5920867995cSHans Petter Selasky 5930867995cSHans Petter Selasky error = usbd_do_request_flags(sc->sc_udev, NULL, 5940867995cSHans Petter Selasky &req, buf, 0, NULL, USB_DEFAULT_TIMEOUT); 5950867995cSHans Petter Selasky 5960867995cSHans Petter Selasky DPRINTF("%s\n", usbd_errstr(error)); 5970867995cSHans Petter Selasky 5980867995cSHans Petter Selasky return (error); 5990867995cSHans Petter Selasky } 6000867995cSHans Petter Selasky 6010867995cSHans Petter Selasky static int 6020867995cSHans Petter Selasky udl_poll(struct udl_softc *sc, uint32_t *buf) 6030867995cSHans Petter Selasky { 6040867995cSHans Petter Selasky uint32_t lbuf; 6050867995cSHans Petter Selasky int error; 6060867995cSHans Petter Selasky 6070867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6080867995cSHans Petter Selasky UDL_CTRL_CMD_POLL, 0x0000, 0x0000, (uint8_t *)&lbuf, sizeof(lbuf)); 6090867995cSHans Petter Selasky if (error == USB_ERR_NORMAL_COMPLETION) 6100867995cSHans Petter Selasky *buf = le32toh(lbuf); 6110867995cSHans Petter Selasky return (error); 6120867995cSHans Petter Selasky } 6130867995cSHans Petter Selasky 6140867995cSHans Petter Selasky static int 6150867995cSHans Petter Selasky udl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf) 6160867995cSHans Petter Selasky { 6170867995cSHans Petter Selasky uint8_t lbuf[1]; 6180867995cSHans Petter Selasky int error; 6190867995cSHans Petter Selasky 6200867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6210867995cSHans Petter Selasky UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1); 6220867995cSHans Petter Selasky if (error == USB_ERR_NORMAL_COMPLETION) 6230867995cSHans Petter Selasky *buf = *(uint8_t *)lbuf; 6240867995cSHans Petter Selasky return (error); 6250867995cSHans Petter Selasky } 6260867995cSHans Petter Selasky 6270867995cSHans Petter Selasky static int 6280867995cSHans Petter Selasky udl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf) 6290867995cSHans Petter Selasky { 6300867995cSHans Petter Selasky int error; 6310867995cSHans Petter Selasky 6320867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 6330867995cSHans Petter Selasky UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1); 6340867995cSHans Petter Selasky return (error); 6350867995cSHans Petter Selasky } 6360867995cSHans Petter Selasky 6370867995cSHans Petter Selasky static int 6380867995cSHans Petter Selasky udl_read_edid(struct udl_softc *sc, uint8_t *buf) 6390867995cSHans Petter Selasky { 6400867995cSHans Petter Selasky uint8_t lbuf[64]; 6410867995cSHans Petter Selasky uint16_t offset; 6420867995cSHans Petter Selasky int error; 6430867995cSHans Petter Selasky 6440867995cSHans Petter Selasky offset = 0; 6450867995cSHans Petter Selasky 6460867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6470867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 6480867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 6490867995cSHans Petter Selasky goto fail; 6500867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 63); 6510867995cSHans Petter Selasky offset += 63; 6520867995cSHans Petter Selasky 6530867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6540867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64); 6550867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 6560867995cSHans Petter Selasky goto fail; 6570867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 63); 6580867995cSHans Petter Selasky offset += 63; 6590867995cSHans Petter Selasky 6600867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, 6610867995cSHans Petter Selasky UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3); 6620867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 6630867995cSHans Petter Selasky goto fail; 6640867995cSHans Petter Selasky bcopy(lbuf + 1, buf + offset, 2); 6650867995cSHans Petter Selasky fail: 6660867995cSHans Petter Selasky return (error); 6670867995cSHans Petter Selasky } 6680867995cSHans Petter Selasky 6690867995cSHans Petter Selasky static uint8_t 6700867995cSHans Petter Selasky udl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t hz, 6710867995cSHans Petter Selasky uint16_t chip, uint32_t clock) 6720867995cSHans Petter Selasky { 6730867995cSHans Petter Selasky uint8_t idx; 6740867995cSHans Petter Selasky 6750867995cSHans Petter Selasky /* 6760867995cSHans Petter Selasky * Check first if we have a matching mode with pixelclock 6770867995cSHans Petter Selasky */ 6780867995cSHans Petter Selasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 6790867995cSHans Petter Selasky if ((udl_modes[idx].hdisplay == hdisplay) && 6800867995cSHans Petter Selasky (udl_modes[idx].vdisplay == vdisplay) && 6810867995cSHans Petter Selasky (udl_modes[idx].clock == clock) && 6820867995cSHans Petter Selasky (udl_modes[idx].chip <= chip)) { 6830867995cSHans Petter Selasky return (idx); 6840867995cSHans Petter Selasky } 6850867995cSHans Petter Selasky } 6860867995cSHans Petter Selasky 6870867995cSHans Petter Selasky /* 6880867995cSHans Petter Selasky * If not, check for matching mode with update frequency 6890867995cSHans Petter Selasky */ 6900867995cSHans Petter Selasky for (idx = 0; idx != UDL_MAX_MODES; idx++) { 6910867995cSHans Petter Selasky if ((udl_modes[idx].hdisplay == hdisplay) && 6920867995cSHans Petter Selasky (udl_modes[idx].vdisplay == vdisplay) && 6930867995cSHans Petter Selasky (udl_modes[idx].hz == hz) && 6940867995cSHans Petter Selasky (udl_modes[idx].chip <= chip)) { 6950867995cSHans Petter Selasky return (idx); 6960867995cSHans Petter Selasky } 6970867995cSHans Petter Selasky } 6980867995cSHans Petter Selasky return (idx); 6990867995cSHans Petter Selasky } 7000867995cSHans Petter Selasky 7010867995cSHans Petter Selasky static void 7020867995cSHans Petter Selasky udl_select_chip(struct udl_softc *sc, struct usb_attach_arg *uaa) 7030867995cSHans Petter Selasky { 7040867995cSHans Petter Selasky const char *pserial; 7050867995cSHans Petter Selasky 7060867995cSHans Petter Selasky pserial = usb_get_serial(uaa->device); 7070867995cSHans Petter Selasky 7080867995cSHans Petter Selasky sc->sc_chip = DL120; 7090867995cSHans Petter Selasky 7100867995cSHans Petter Selasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 7110867995cSHans Petter Selasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_WSDVI)) { 7120867995cSHans Petter Selasky 7130867995cSHans Petter Selasky /* 7140867995cSHans Petter Selasky * WS Tech DVI is DL120 or DL160. All deviced uses the 7150867995cSHans Petter Selasky * same revision (0.04) so iSerialNumber must be used 7160867995cSHans Petter Selasky * to determin which chip it is. 7170867995cSHans Petter Selasky */ 7180867995cSHans Petter Selasky 7190867995cSHans Petter Selasky if (strlen(pserial) > 7) { 7200867995cSHans Petter Selasky if (strncmp(pserial, "0198-13", 7) == 0) 7210867995cSHans Petter Selasky sc->sc_chip = DL160; 7220867995cSHans Petter Selasky } 7230867995cSHans Petter Selasky DPRINTF("iSerialNumber (%s) used to select chip (%d)\n", 7240867995cSHans Petter Selasky pserial, sc->sc_chip); 7250867995cSHans Petter Selasky } 7260867995cSHans Petter Selasky if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) && 7270867995cSHans Petter Selasky (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_SWDVI)) { 7280867995cSHans Petter Selasky 7290867995cSHans Petter Selasky /* 7300867995cSHans Petter Selasky * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision 7310867995cSHans Petter Selasky * can be used to differ between DL1x0 and DL1x5. Minor to 7320867995cSHans Petter Selasky * differ between DL1x5. iSerialNumber seems not to be uniqe. 7330867995cSHans Petter Selasky */ 7340867995cSHans Petter Selasky 7350867995cSHans Petter Selasky sc->sc_chip = DL160; 7360867995cSHans Petter Selasky 7370867995cSHans Petter Selasky if (uaa->info.bcdDevice >= 0x100) { 7380867995cSHans Petter Selasky sc->sc_chip = DL165; 7390867995cSHans Petter Selasky if (uaa->info.bcdDevice == 0x104) 7400867995cSHans Petter Selasky sc->sc_chip = DL195; 7410867995cSHans Petter Selasky if (uaa->info.bcdDevice == 0x108) 7420867995cSHans Petter Selasky sc->sc_chip = DL125; 7430867995cSHans Petter Selasky } 7440867995cSHans Petter Selasky DPRINTF("bcdDevice (%02x) used to select chip (%d)\n", 7450867995cSHans Petter Selasky uaa->info.bcdDevice, sc->sc_chip); 7460867995cSHans Petter Selasky } 7470867995cSHans Petter Selasky } 7480867995cSHans Petter Selasky 7490867995cSHans Petter Selasky static int 7500867995cSHans Petter Selasky udl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len) 7510867995cSHans Petter Selasky { 7520867995cSHans Petter Selasky int error; 7530867995cSHans Petter Selasky 7540867995cSHans Petter Selasky error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, 7550867995cSHans Petter Selasky UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len); 7560867995cSHans Petter Selasky return (error); 7570867995cSHans Petter Selasky } 7580867995cSHans Petter Selasky 7590867995cSHans Petter Selasky static void 7600867995cSHans Petter Selasky udl_fbmem_alloc(struct udl_softc *sc) 7610867995cSHans Petter Selasky { 7620867995cSHans Petter Selasky uint32_t size; 7630867995cSHans Petter Selasky 7640867995cSHans Petter Selasky size = udl_get_fb_size(sc); 7650867995cSHans Petter Selasky size = round_page(size); 7660867995cSHans Petter Selasky 7670867995cSHans Petter Selasky sc->sc_fb_addr = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); 7680867995cSHans Petter Selasky sc->sc_fb_copy = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); 7690867995cSHans Petter Selasky sc->sc_fb_size = size; 7700867995cSHans Petter Selasky } 7710867995cSHans Petter Selasky 7720867995cSHans Petter Selasky static void 7730867995cSHans Petter Selasky udl_cmd_insert_int_1(struct udl_cmd_buf *cb, uint8_t value) 7740867995cSHans Petter Selasky { 7750867995cSHans Petter Selasky 7760867995cSHans Petter Selasky cb->buf[cb->off] = value; 7770867995cSHans Petter Selasky cb->off += 1; 7780867995cSHans Petter Selasky } 7790867995cSHans Petter Selasky 7800867995cSHans Petter Selasky #if 0 7810867995cSHans Petter Selasky static void 7820867995cSHans Petter Selasky udl_cmd_insert_int_2(struct udl_cmd_buf *cb, uint16_t value) 7830867995cSHans Petter Selasky { 7840867995cSHans Petter Selasky uint16_t lvalue; 7850867995cSHans Petter Selasky 7860867995cSHans Petter Selasky lvalue = htobe16(value); 7870867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 2); 7880867995cSHans Petter Selasky 7890867995cSHans Petter Selasky cb->off += 2; 7900867995cSHans Petter Selasky } 7910867995cSHans Petter Selasky 7920867995cSHans Petter Selasky #endif 7930867995cSHans Petter Selasky 7940867995cSHans Petter Selasky static void 7950867995cSHans Petter Selasky udl_cmd_insert_int_3(struct udl_cmd_buf *cb, uint32_t value) 7960867995cSHans Petter Selasky { 7970867995cSHans Petter Selasky uint32_t lvalue; 7980867995cSHans Petter Selasky 7990867995cSHans Petter Selasky #if BYTE_ORDER == BIG_ENDIAN 8000867995cSHans Petter Selasky lvalue = htobe32(value) << 8; 8010867995cSHans Petter Selasky #else 8020867995cSHans Petter Selasky lvalue = htobe32(value) >> 8; 8030867995cSHans Petter Selasky #endif 8040867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 3); 8050867995cSHans Petter Selasky 8060867995cSHans Petter Selasky cb->off += 3; 8070867995cSHans Petter Selasky } 8080867995cSHans Petter Selasky 8090867995cSHans Petter Selasky #if 0 8100867995cSHans Petter Selasky static void 8110867995cSHans Petter Selasky udl_cmd_insert_int_4(struct udl_cmd_buf *cb, uint32_t value) 8120867995cSHans Petter Selasky { 8130867995cSHans Petter Selasky uint32_t lvalue; 8140867995cSHans Petter Selasky 8150867995cSHans Petter Selasky lvalue = htobe32(value); 8160867995cSHans Petter Selasky bcopy(&lvalue, cb->buf + cb->off, 4); 8170867995cSHans Petter Selasky 8180867995cSHans Petter Selasky cb->off += 4; 8190867995cSHans Petter Selasky } 8200867995cSHans Petter Selasky 8210867995cSHans Petter Selasky #endif 8220867995cSHans Petter Selasky 8230867995cSHans Petter Selasky static void 8240867995cSHans Petter Selasky udl_cmd_insert_buf_le16(struct udl_cmd_buf *cb, const uint8_t *buf, uint32_t len) 8250867995cSHans Petter Selasky { 8260867995cSHans Petter Selasky uint32_t x; 8270867995cSHans Petter Selasky 8280867995cSHans Petter Selasky for (x = 0; x != len; x += 2) { 8290867995cSHans Petter Selasky /* byte swap from little endian to big endian */ 8300867995cSHans Petter Selasky cb->buf[cb->off + x + 0] = buf[x + 1]; 8310867995cSHans Petter Selasky cb->buf[cb->off + x + 1] = buf[x + 0]; 8320867995cSHans Petter Selasky } 8330867995cSHans Petter Selasky cb->off += len; 8340867995cSHans Petter Selasky } 8350867995cSHans Petter Selasky 8360867995cSHans Petter Selasky static void 8370867995cSHans Petter Selasky udl_cmd_write_reg_1(struct udl_cmd_buf *cb, uint8_t reg, uint8_t val) 8380867995cSHans Petter Selasky { 8390867995cSHans Petter Selasky 8400867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 8410867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_REG_WRITE_1); 8420867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, reg); 8430867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, val); 8440867995cSHans Petter Selasky } 8450867995cSHans Petter Selasky 8460867995cSHans Petter Selasky static void 8470867995cSHans Petter Selasky udl_cmd_write_reg_3(struct udl_cmd_buf *cb, uint8_t reg, uint32_t val) 8480867995cSHans Petter Selasky { 8490867995cSHans Petter Selasky 8500867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 0, (val >> 16) & 0xff); 8510867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 1, (val >> 8) & 0xff); 8520867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, reg + 2, (val >> 0) & 0xff); 8530867995cSHans Petter Selasky } 8540867995cSHans Petter Selasky 8550867995cSHans Petter Selasky static int 8560867995cSHans Petter Selasky udl_init_chip(struct udl_softc *sc) 8570867995cSHans Petter Selasky { 8580867995cSHans Petter Selasky uint32_t ui32; 8590867995cSHans Petter Selasky uint8_t ui8; 8600867995cSHans Petter Selasky int error; 8610867995cSHans Petter Selasky 8620867995cSHans Petter Selasky error = udl_poll(sc, &ui32); 8630867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 8640867995cSHans Petter Selasky return (error); 8650867995cSHans Petter Selasky DPRINTF("poll=0x%08x\n", ui32); 8660867995cSHans Petter Selasky 8670867995cSHans Petter Selasky /* Some products may use later chip too */ 8680867995cSHans Petter Selasky switch (ui32 & 0xff) { 8690867995cSHans Petter Selasky case 0xf1: /* DL1x5 */ 8700867995cSHans Petter Selasky switch (sc->sc_chip) { 8710867995cSHans Petter Selasky case DL120: 8720867995cSHans Petter Selasky sc->sc_chip = DL125; 8730867995cSHans Petter Selasky break; 8740867995cSHans Petter Selasky case DL160: 8750867995cSHans Petter Selasky sc->sc_chip = DL165; 8760867995cSHans Petter Selasky break; 8770867995cSHans Petter Selasky } 8780867995cSHans Petter Selasky break; 8790867995cSHans Petter Selasky } 8800867995cSHans Petter Selasky DPRINTF("chip 0x%04x\n", sc->sc_chip); 8810867995cSHans Petter Selasky 8820867995cSHans Petter Selasky error = udl_read_1(sc, 0xc484, &ui8); 8830867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 8840867995cSHans Petter Selasky return (error); 8850867995cSHans Petter Selasky DPRINTF("read 0x%02x from 0xc484\n", ui8); 8860867995cSHans Petter Selasky 8870867995cSHans Petter Selasky error = udl_write_1(sc, 0xc41f, 0x01); 8880867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 8890867995cSHans Petter Selasky return (error); 8900867995cSHans Petter Selasky DPRINTF("write 0x01 to 0xc41f\n"); 8910867995cSHans Petter Selasky 8920867995cSHans Petter Selasky error = udl_read_edid(sc, sc->sc_edid); 8930867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 8940867995cSHans Petter Selasky return (error); 8950867995cSHans Petter Selasky DPRINTF("read EDID\n"); 8960867995cSHans Petter Selasky 8970867995cSHans Petter Selasky error = udl_set_enc_key(sc, __DECONST(void *, udl_null_key_1), 8980867995cSHans Petter Selasky sizeof(udl_null_key_1)); 8990867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9000867995cSHans Petter Selasky return (error); 9010867995cSHans Petter Selasky DPRINTF("set encryption key\n"); 9020867995cSHans Petter Selasky 9030867995cSHans Petter Selasky error = udl_write_1(sc, 0xc40b, 0x00); 9040867995cSHans Petter Selasky if (error != USB_ERR_NORMAL_COMPLETION) 9050867995cSHans Petter Selasky return (error); 9060867995cSHans Petter Selasky DPRINTF("write 0x00 to 0xc40b\n"); 9070867995cSHans Petter Selasky 9080867995cSHans Petter Selasky return (USB_ERR_NORMAL_COMPLETION); 9090867995cSHans Petter Selasky } 9100867995cSHans Petter Selasky 9110867995cSHans Petter Selasky static void 9120867995cSHans Petter Selasky udl_init_fb_offsets(struct udl_cmd_buf *cb, uint32_t start16, uint32_t stride16, 9130867995cSHans Petter Selasky uint32_t start8, uint32_t stride8) 9140867995cSHans Petter Selasky { 9150867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 9160867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START16, start16); 9170867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE16, stride16); 9180867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START8, start8); 9190867995cSHans Petter Selasky udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE8, stride8); 9200867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 9210867995cSHans Petter Selasky } 9220867995cSHans Petter Selasky 9230867995cSHans Petter Selasky static int 9240867995cSHans Petter Selasky udl_init_resolution(struct udl_softc *sc) 9250867995cSHans Petter Selasky { 9260867995cSHans Petter Selasky const uint32_t max = udl_get_fb_size(sc); 9270867995cSHans Petter Selasky const uint8_t *buf = udl_modes[sc->sc_cur_mode].mode; 9280867995cSHans Petter Selasky struct udl_cmd_buf *cb; 9290867995cSHans Petter Selasky uint32_t delta; 9300867995cSHans Petter Selasky uint32_t i; 9310867995cSHans Petter Selasky int error; 9320867995cSHans Petter Selasky 9330867995cSHans Petter Selasky /* get new buffer */ 9340867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 9350867995cSHans Petter Selasky if (cb == NULL) 9360867995cSHans Petter Selasky return (EAGAIN); 9370867995cSHans Petter Selasky 9380867995cSHans Petter Selasky /* write resolution values and set video memory offsets */ 9390867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00); 9400867995cSHans Petter Selasky for (i = 0; i < UDL_MODE_SIZE; i++) 9410867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, i, buf[i]); 9420867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 9430867995cSHans Petter Selasky 9440867995cSHans Petter Selasky udl_init_fb_offsets(cb, 0x000000, 0x000a00, 0x555555, 0x000500); 9450867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 9460867995cSHans Petter Selasky 9470867995cSHans Petter Selasky /* fill screen with black color */ 9480867995cSHans Petter Selasky for (i = 0; i < max; i += delta) { 9490867995cSHans Petter Selasky static const uint8_t udl_black[UDL_CMD_MAX_PIXEL_COUNT * 2] __aligned(4); 9500867995cSHans Petter Selasky 9510867995cSHans Petter Selasky delta = max - i; 9520867995cSHans Petter Selasky if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2) 9530867995cSHans Petter Selasky delta = UDL_CMD_MAX_PIXEL_COUNT * 2; 9540867995cSHans Petter Selasky if (i == 0) 9550867995cSHans Petter Selasky error = udl_cmd_write_buf_le16(sc, udl_black, i, delta / 2, M_WAITOK); 9560867995cSHans Petter Selasky else 9570867995cSHans Petter Selasky error = udl_cmd_buf_copy_le16(sc, 0, i, delta / 2, M_WAITOK); 9580867995cSHans Petter Selasky if (error) 9590867995cSHans Petter Selasky return (error); 9600867995cSHans Petter Selasky } 9610867995cSHans Petter Selasky 9620867995cSHans Petter Selasky /* get new buffer */ 9630867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, M_WAITOK); 9640867995cSHans Petter Selasky if (cb == NULL) 9650867995cSHans Petter Selasky return (EAGAIN); 9660867995cSHans Petter Selasky 9670867995cSHans Petter Selasky /* show framebuffer content */ 9680867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON); 9690867995cSHans Petter Selasky udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff); 9700867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 9710867995cSHans Petter Selasky return (0); 9720867995cSHans Petter Selasky } 9730867995cSHans Petter Selasky 9740867995cSHans Petter Selasky static void 9750867995cSHans Petter Selasky udl_select_mode(struct udl_softc *sc) 9760867995cSHans Petter Selasky { 9770867995cSHans Petter Selasky struct udl_mode mode; 9780867995cSHans Petter Selasky int index = UDL_MAX_MODES; 9790867995cSHans Petter Selasky int i; 9800867995cSHans Petter Selasky 9810867995cSHans Petter Selasky /* try to get the preferred mode from EDID */ 9820867995cSHans Petter Selasky edid_parse(sc->sc_edid, &sc->sc_edid_info); 9830867995cSHans Petter Selasky #ifdef USB_DEBUG 9840867995cSHans Petter Selasky edid_print(&sc->sc_edid_info); 9850867995cSHans Petter Selasky #endif 9860867995cSHans Petter Selasky if (sc->sc_edid_info.edid_preferred_mode != NULL) { 9870867995cSHans Petter Selasky mode.hz = 9880867995cSHans Petter Selasky (sc->sc_edid_info.edid_preferred_mode->dot_clock * 1000) / 9890867995cSHans Petter Selasky (sc->sc_edid_info.edid_preferred_mode->htotal * 9900867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->vtotal); 9910867995cSHans Petter Selasky mode.clock = 9920867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->dot_clock / 10; 9930867995cSHans Petter Selasky mode.hdisplay = 9940867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->hdisplay; 9950867995cSHans Petter Selasky mode.vdisplay = 9960867995cSHans Petter Selasky sc->sc_edid_info.edid_preferred_mode->vdisplay; 9970867995cSHans Petter Selasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, mode.hz, 9980867995cSHans Petter Selasky sc->sc_chip, mode.clock); 9990867995cSHans Petter Selasky sc->sc_cur_mode = index; 10000867995cSHans Petter Selasky } else { 10010867995cSHans Petter Selasky DPRINTF("no preferred mode found!\n"); 10020867995cSHans Petter Selasky } 10030867995cSHans Petter Selasky 10040867995cSHans Petter Selasky if (index == UDL_MAX_MODES) { 10050867995cSHans Petter Selasky DPRINTF("no mode line found for %dx%d @ %dHz!\n", 10060867995cSHans Petter Selasky mode.hdisplay, mode.vdisplay, mode.hz); 10070867995cSHans Petter Selasky 10080867995cSHans Petter Selasky i = 0; 10090867995cSHans Petter Selasky while (i < sc->sc_edid_info.edid_nmodes) { 10100867995cSHans Petter Selasky mode.hz = 10110867995cSHans Petter Selasky (sc->sc_edid_info.edid_modes[i].dot_clock * 1000) / 10120867995cSHans Petter Selasky (sc->sc_edid_info.edid_modes[i].htotal * 10130867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].vtotal); 10140867995cSHans Petter Selasky mode.clock = 10150867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].dot_clock / 10; 10160867995cSHans Petter Selasky mode.hdisplay = 10170867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].hdisplay; 10180867995cSHans Petter Selasky mode.vdisplay = 10190867995cSHans Petter Selasky sc->sc_edid_info.edid_modes[i].vdisplay; 10200867995cSHans Petter Selasky index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, 10210867995cSHans Petter Selasky mode.hz, sc->sc_chip, mode.clock); 10220867995cSHans Petter Selasky if (index < UDL_MAX_MODES) 10230867995cSHans Petter Selasky if ((sc->sc_cur_mode == UDL_MAX_MODES) || 10240867995cSHans Petter Selasky (index > sc->sc_cur_mode)) 10250867995cSHans Petter Selasky sc->sc_cur_mode = index; 10260867995cSHans Petter Selasky i++; 10270867995cSHans Petter Selasky } 10280867995cSHans Petter Selasky } 10290867995cSHans Petter Selasky /* 10300867995cSHans Petter Selasky * If no mode found use default. 10310867995cSHans Petter Selasky */ 10320867995cSHans Petter Selasky if (sc->sc_cur_mode == UDL_MAX_MODES) 10330867995cSHans Petter Selasky sc->sc_cur_mode = udl_lookup_mode(800, 600, 60, sc->sc_chip, 0); 10340867995cSHans Petter Selasky } 10350867995cSHans Petter Selasky 10360867995cSHans Petter Selasky static int 10370867995cSHans Petter Selasky udl_cmd_write_buf_le16(struct udl_softc *sc, const uint8_t *buf, uint32_t off, 10380867995cSHans Petter Selasky uint8_t pixels, int flags) 10390867995cSHans Petter Selasky { 10400867995cSHans Petter Selasky struct udl_cmd_buf *cb; 10410867995cSHans Petter Selasky 10420867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 10430867995cSHans Petter Selasky if (cb == NULL) 10440867995cSHans Petter Selasky return (EAGAIN); 10450867995cSHans Petter Selasky 10460867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 10470867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD); 10480867995cSHans Petter Selasky udl_cmd_insert_int_3(cb, off); 10490867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, pixels); 10500867995cSHans Petter Selasky udl_cmd_insert_buf_le16(cb, buf, 2 * pixels); 10510867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 10520867995cSHans Petter Selasky 10530867995cSHans Petter Selasky return (0); 10540867995cSHans Petter Selasky } 10550867995cSHans Petter Selasky 10560867995cSHans Petter Selasky static int 10570867995cSHans Petter Selasky udl_cmd_buf_copy_le16(struct udl_softc *sc, uint32_t src, uint32_t dst, 10580867995cSHans Petter Selasky uint8_t pixels, int flags) 10590867995cSHans Petter Selasky { 10600867995cSHans Petter Selasky struct udl_cmd_buf *cb; 10610867995cSHans Petter Selasky 10620867995cSHans Petter Selasky cb = udl_cmd_buf_alloc(sc, flags); 10630867995cSHans Petter Selasky if (cb == NULL) 10640867995cSHans Petter Selasky return (EAGAIN); 10650867995cSHans Petter Selasky 10660867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_SOC); 10670867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD); 1068*b161d698SHans Petter Selasky udl_cmd_insert_int_3(cb, dst); 10690867995cSHans Petter Selasky udl_cmd_insert_int_1(cb, pixels); 1070*b161d698SHans Petter Selasky udl_cmd_insert_int_3(cb, src); 10710867995cSHans Petter Selasky udl_cmd_buf_send(sc, cb); 10720867995cSHans Petter Selasky 10730867995cSHans Petter Selasky return (0); 10740867995cSHans Petter Selasky } 1075