Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 15 Feb 2015 12:02:18 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r278799 - in head: share/man/man4 sys/conf sys/dev/usb sys/dev/usb/video sys/modules/usb/udl
Message-ID:  <201502151202.t1FC2IcA099300@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Sun Feb 15 12:02:17 2015
New Revision: 278799
URL: https://svnweb.freebsd.org/changeset/base/278799

Log:
  Import USB display link driver from OpenBSD. Support for compression
  has been removed and the driver has been greatly simplified and
  optimised for FreeBSD. The driver is currently not built by default.
  
  Requested by:	Bruce Simpson <bms@fastmail.net>

Added:
  head/share/man/man4/udl.4   (contents, props changed)
  head/sys/dev/usb/video/
  head/sys/dev/usb/video/udl.c   (contents, props changed)
  head/sys/dev/usb/video/udl.h   (contents, props changed)
  head/sys/modules/usb/udl/
  head/sys/modules/usb/udl/Makefile   (contents, props changed)
Modified:
  head/share/man/man4/Makefile
  head/sys/conf/files
  head/sys/dev/usb/usbdevs

Modified: head/share/man/man4/Makefile
==============================================================================
--- head/share/man/man4/Makefile	Sun Feb 15 11:37:40 2015	(r278798)
+++ head/share/man/man4/Makefile	Sun Feb 15 12:02:17 2015	(r278799)
@@ -843,6 +843,7 @@ MAN+=	\
 	udbp.4 \
 	udp.4 \
 	udplite.4 \
+	udl.4 \
 	uep.4 \
 	ufm.4 \
 	ufoma.4 \

Added: head/share/man/man4/udl.4
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/share/man/man4/udl.4	Sun Feb 15 12:02:17 2015	(r278799)
@@ -0,0 +1,67 @@
+.\" $OpenBSD: udl.4,v 1.20 2012/09/18 17:11:41 jasper Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd February 15, 2015
+.Dt UDL 4
+.Os
+.Sh NAME
+.Nm udl
+.Nd DisplayLink DL-120 / DL-160 USB display devices
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device udl"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+udl_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver supports USB display devices based on the DisplayLink DL-120 / DL-160
+graphic chip.
+.Sh HARDWARE
+The following devices should work:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It Century Corp. Japan Plus One LCD-8000U
+.It Century Corp. Japan Plus One LCD-4300U
+.It DisplayLink USB to DVI
+.It ForwardVideo EasyCAP008 USB to DVI
+.It HP USB 2.0 Docking Station (FQ834)
+.It HP USB Graphics Adapter (NL571)
+.It IOGEAR USB 2.0 External DVI (GUC2020)
+.It Koenig CMP-USBVGA10 and CMP-USBVGA11
+.It Lenovo 45K5296 USB to DVI
+.It Lenovo ThinkVision LT1421
+.It Lilliput UM-70
+.It Nanovision MiMo UM-710 and UM-740
+.It Rextron VCUD60 USB to DVI
+.It Samsung LD220
+.It StarTech CONV-USB2DVI
+.It Sunweit USB to DVI
+.It Unitek Y-2240 USB to DVI
+.It VideoHome NBdock1920
+.El
+.Sh SEE ALSO
+.Xr usb 4

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Sun Feb 15 11:37:40 2015	(r278798)
+++ head/sys/conf/files	Sun Feb 15 12:02:17 2015	(r278799)
@@ -2563,6 +2563,10 @@ dev/usb/template/usb_template_mtp.c	opti
 dev/usb/template/usb_template_phone.c	optional usb_template
 dev/usb/template/usb_template_serialnet.c	optional usb_template
 #
+# USB video drivers
+#
+dev/usb/video/udl.c			optional udl
+#
 # USB END
 #
 dev/videomode/videomode.c		optional videomode

Modified: head/sys/dev/usb/usbdevs
==============================================================================
--- head/sys/dev/usb/usbdevs	Sun Feb 15 11:37:40 2015	(r278798)
+++ head/sys/dev/usb/usbdevs	Sun Feb 15 12:02:17 2015	(r278799)
@@ -686,6 +686,7 @@ vendor ASUS2		0x1761	ASUS
 vendor SWEEX2		0x177f	Sweex
 vendor METAGEEK		0x1781	MetaGeek
 vendor KAMSTRUP		0x17a8	Kamstrup A/S
+vendor DISPLAYLINK	0x17e9	DisplayLink
 vendor LENOVO		0x17ef	Lenovo
 vendor WAVESENSE	0x17f4	WaveSense
 vendor VAISALA		0x1843	Vaisala
@@ -1655,6 +1656,27 @@ product DLINK2 RT3070_4		0x3c15	RT3070
 product DLINK2 RT3070_5		0x3c16	RT3070
 product DLINK3 DWM652		0x3e04	DWM-652
 
+/* DisplayLink products */
+product DISPLAYLINK LCD4300U	0x01ba	LCD-4300U
+product DISPLAYLINK LCD8000U	0x01bb	LCD-8000U
+product DISPLAYLINK LD220	0x0100	Samsung LD220
+product DISPLAYLINK GUC2020	0x0059	IOGEAR DVI GUC2020
+product DISPLAYLINK VCUD60	0x0136	Rextron DVI
+product DISPLAYLINK CONV	0x0138	StarTech CONV-USB2DVI
+product DISPLAYLINK DLDVI	0x0141	DisplayLink DVI
+product DISPLAYLINK VGA10	0x015a	CMP-USBVGA10
+product DISPLAYLINK WSDVI	0x0198	WS Tech DVI
+product DISPLAYLINK EC008	0x019b	EasyCAP008 DVI
+product DISPLAYLINK HPDOCK	0x01d4	HP USB Docking
+product DISPLAYLINK NL571	0x01d7	HP USB DVI
+product DISPLAYLINK M01061	0x01e2	Lenovo DVI
+product DISPLAYLINK SWDVI	0x024c	SUNWEIT DVI
+product DISPLAYLINK NBDOCK	0x0215	VideoHome NBdock1920
+product DISPLAYLINK LUM70	0x02a9	Lilliput UM-70
+product DISPLAYLINK UM7X0	0x401a	nanovision MiMo
+product DISPLAYLINK LT1421	0x03e0	Lenovo ThinkVision LT1421
+product DISPLAYLINK POLARIS2	0x0117	Polaris2 USB dock
+
 /* DMI products */
 product DMI CFSM_RW		0xa109	CF/SM Reader/Writer
 product DMI DISK		0x2bcf	Generic Disk

Added: head/sys/dev/usb/video/udl.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/usb/video/udl.c	Sun Feb 15 12:02:17 2015	(r278799)
@@ -0,0 +1,1068 @@
+/*	$OpenBSD: udl.c,v 1.81 2014/12/09 07:05:06 doug Exp $ */
+/*	$FreeBSD$ */
+
+/*-
+ * Copyright (c) 2015 Hans Petter Selasky <hselasky@freebsd.org>
+ * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for the "DisplayLink DL-120 / DL-160" graphic chips based on
+ * the reversed engineered specifications of Florian Echtler
+ * <floe@butterbrot.org>:
+ *
+ * 	http://floe.butterbrot.org/displaylink/doku.php
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/consio.h>
+#include <sys/fbio.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/syscons/syscons.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/video/udl.h>
+
+#include "fb_if.h"
+
+#undef DPRINTF
+#undef DPRINTFN
+#define	USB_DEBUG_VAR udl_debug
+#include <dev/usb/usb_debug.h>
+
+#ifdef USB_DEBUG
+static int udl_debug = 0;
+
+static	SYSCTL_NODE(_hw_usb, OID_AUTO, udl, CTLFLAG_RW, 0, "USB UDL");
+
+SYSCTL_INT(_hw_usb_udl, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &udl_debug, 0, "Debug level");
+#endif
+
+/*
+ * Prototypes.
+ */
+static usb_callback_t udl_bulk_write_callback;
+
+static device_probe_t udl_probe;
+static device_attach_t udl_attach;
+static device_detach_t udl_detach;
+static fb_getinfo_t udl_fb_getinfo;
+#if 0
+static fb_blank_display_t udl_fb_blank_display;
+#endif
+
+static void udl_select_chip(struct udl_softc *, struct usb_attach_arg *);
+static int udl_init_chip(struct udl_softc *);
+static void udl_select_mode(struct udl_softc *);
+static int udl_init_resolution(struct udl_softc *);
+static void udl_fbmem_alloc(struct udl_softc *);
+static int udl_cmd_write_buf_le16(struct udl_softc *, const uint8_t *, uint32_t, uint8_t, int);
+static int udl_cmd_buf_copy_le16(struct udl_softc *, uint32_t, uint32_t, uint8_t, int);
+static void udl_cmd_insert_int_1(struct udl_cmd_buf *, uint8_t);
+static void udl_cmd_insert_int_3(struct udl_cmd_buf *, uint32_t);
+static void udl_cmd_insert_buf_le16(struct udl_cmd_buf *, const uint8_t *, uint32_t);
+static void udl_cmd_write_reg_1(struct udl_cmd_buf *, uint8_t, uint8_t);
+static void udl_cmd_write_reg_3(struct udl_cmd_buf *, uint8_t, uint32_t);
+#if 0
+static int udl_power_save(struct udl_softc *, int, int);
+#endif
+
+static const struct usb_config udl_config[UDL_N_TRANSFER] = {
+	[UDL_BULK_WRITE_0] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_TX,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+		.bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES,
+		.callback = &udl_bulk_write_callback,
+		.frames = UDL_CMD_MAX_FRAMES,
+		.timeout = 5000,	/* 5 seconds */
+	},
+	[UDL_BULK_WRITE_1] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_TX,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+		.bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES,
+		.callback = &udl_bulk_write_callback,
+		.frames = UDL_CMD_MAX_FRAMES,
+		.timeout = 5000,	/* 5 seconds */
+	},
+};
+
+/*
+ * Driver glue.
+ */
+static devclass_t udl_devclass;
+
+static device_method_t udl_methods[] = {
+	DEVMETHOD(device_probe, udl_probe),
+	DEVMETHOD(device_attach, udl_attach),
+	DEVMETHOD(device_detach, udl_detach),
+	DEVMETHOD(fb_getinfo, udl_fb_getinfo),
+#if 0
+	DEVMETHOD(fb_blank_display, udl_fb_blank_display),
+#endif
+	DEVMETHOD_END
+};
+
+static driver_t udl_driver = {
+	.name = "udl",
+	.methods = udl_methods,
+	.size = sizeof(struct udl_softc),
+};
+
+DRIVER_MODULE(udl, uhub, udl_driver, udl_devclass, NULL, NULL);
+MODULE_DEPEND(udl, usb, 1, 1, 1);
+MODULE_DEPEND(udl, fbd, 1, 1, 1);
+MODULE_DEPEND(udl, videomode, 1, 1, 1);
+MODULE_VERSION(udl, 1);
+
+/*
+ * Matching devices.
+ */
+static const STRUCT_USB_HOST_ID udl_devs[] = {
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U, DL120)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U, DL120)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020, DL160)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220, DL165)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60, DL160)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI, DL160)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10, DL120)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI, DLUNK)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008, DL160)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK, DL160)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571, DL160)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061, DL195)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK, DL165)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI, DLUNK)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0, DL120)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV, DL160)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70, DL125)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2, DLUNK)},
+	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)}
+};
+
+static uint32_t
+udl_get_fb_size(struct udl_softc *sc)
+{
+	unsigned i = sc->sc_cur_mode;
+
+	return ((uint32_t)udl_modes[i].hdisplay *
+	    (uint32_t)udl_modes[i].vdisplay * 2);
+}
+
+static uint32_t
+udl_get_fb_width(struct udl_softc *sc)
+{
+	unsigned i = sc->sc_cur_mode;
+
+	return (udl_modes[i].vdisplay);
+}
+
+static uint32_t
+udl_get_fb_height(struct udl_softc *sc)
+{
+	unsigned i = sc->sc_cur_mode;
+
+	return (udl_modes[i].hdisplay);
+}
+
+static uint32_t
+udl_get_fb_hz(struct udl_softc *sc)
+{
+	unsigned i = sc->sc_cur_mode;
+
+	return (udl_modes[i].hz);
+}
+
+static void
+udl_callout(void *arg)
+{
+	struct udl_softc *sc = arg;
+	const uint32_t max = udl_get_fb_size(sc);
+
+	if (sc->sc_power_save == 0) {
+		if (sc->sc_sync_off >= max)
+			sc->sc_sync_off = 0;
+		usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]);
+		usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]);
+	}
+	callout_reset(&sc->sc_callout, hz / 5, &udl_callout, sc);
+}
+
+static int
+udl_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST)
+		return (ENXIO);
+	if (uaa->info.bConfigIndex != 0)
+		return (ENXIO);
+	if (uaa->info.bIfaceIndex != 0)
+		return (ENXIO);
+
+	return (usbd_lookup_id_by_uaa(udl_devs, sizeof(udl_devs), uaa));
+}
+
+static int
+udl_attach(device_t dev)
+{
+	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
+	struct sysctl_oid *tree = device_get_sysctl_tree(dev);
+	struct udl_softc *sc = device_get_softc(dev);
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	int error;
+	int i;
+
+	device_set_usb_desc(dev);
+
+	mtx_init(&sc->sc_mtx, "UDL lock", NULL, MTX_DEF);
+	cv_init(&sc->sc_cv, "UDLCV");
+	callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
+	sc->sc_udev = uaa->device;
+
+	error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
+	    sc->sc_xfer, udl_config, UDL_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error));
+		goto detach;
+	}
+	usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_0], &sc->sc_xfer_head[0]);
+	usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_1], &sc->sc_xfer_head[1]);
+
+	TAILQ_INIT(&sc->sc_xfer_head[0]);
+	TAILQ_INIT(&sc->sc_xfer_head[1]);
+	TAILQ_INIT(&sc->sc_cmd_buf_free);
+	TAILQ_INIT(&sc->sc_cmd_buf_pending);
+
+	sc->sc_def_chip = -1;
+	sc->sc_chip = USB_GET_DRIVER_INFO(uaa);
+	sc->sc_def_mode = -1;
+	sc->sc_cur_mode = UDL_MAX_MODES;
+
+	/* Allow chip ID to be overwritten */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid_force",
+	    CTLFLAG_RWTUN, &sc->sc_def_chip, 0, "chip ID");
+
+	/* Export current chip ID */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid",
+	    CTLFLAG_RD, &sc->sc_chip, 0, "chip ID");
+
+	if (sc->sc_def_chip > -1 && sc->sc_def_chip <= DLMAX) {
+		device_printf(dev, "Forcing chip ID to 0x%04x\n", sc->sc_def_chip);
+		sc->sc_chip = sc->sc_def_chip;
+	}
+	/*
+	 * The product might have more than one chip
+	 */
+	if (sc->sc_chip == DLUNK)
+		udl_select_chip(sc, uaa);
+
+	for (i = 0; i != UDL_CMD_MAX_BUFFERS; i++) {
+		struct udl_cmd_buf *cb = &sc->sc_cmd_buf_temp[i];
+
+		TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry);
+	}
+
+	/*
+	 * Initialize chip.
+	 */
+	error = udl_init_chip(sc);
+	if (error != USB_ERR_NORMAL_COMPLETION)
+		goto detach;
+
+	/*
+	 * Select edid mode.
+	 */
+	udl_select_mode(sc);
+
+	/* Allow default mode to be overwritten */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode_force",
+	    CTLFLAG_RWTUN, &sc->sc_def_mode, 0, "mode");
+
+	/* Export current mode */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode",
+	    CTLFLAG_RD, &sc->sc_cur_mode, 0, "mode");
+
+	i = sc->sc_def_mode;
+	if (i > -1 && i < UDL_MAX_MODES) {
+		if (udl_modes[i].chip <= sc->sc_chip) {
+			device_printf(dev, "Forcing mode to %d\n", i);
+			sc->sc_cur_mode = i;
+		}
+	}
+	/* Printout current mode */
+	device_printf(dev, "Mode selected %dx%d @ %dHz\n",
+	    (int)udl_get_fb_width(sc),
+	    (int)udl_get_fb_height(sc),
+	    (int)udl_get_fb_hz(sc));
+
+	udl_init_resolution(sc);
+
+	/* Allocate frame buffer */
+	udl_fbmem_alloc(sc);
+
+	UDL_LOCK(sc);
+	udl_callout(sc);
+	UDL_UNLOCK(sc);
+
+	sc->sc_fb_info.fb_name = device_get_nameunit(dev);
+	sc->sc_fb_info.fb_size = sc->sc_fb_size;
+	sc->sc_fb_info.fb_bpp = 16;
+	sc->sc_fb_info.fb_depth = 16;
+	sc->sc_fb_info.fb_width = udl_get_fb_width(sc);
+	sc->sc_fb_info.fb_height = udl_get_fb_height(sc);
+	sc->sc_fb_info.fb_stride = sc->sc_fb_info.fb_width * 2;
+	sc->sc_fb_info.fb_pbase = (uintptr_t)sc->sc_fb_addr;
+	sc->sc_fb_info.fb_vbase = (uintptr_t)sc->sc_fb_addr;
+
+	sc->sc_fbdev = device_add_child(dev, "fbd", -1);
+	if (sc->sc_fbdev == NULL)
+		goto detach;
+	if (device_probe_and_attach(sc->sc_fbdev) != 0)
+		goto detach;
+
+	return (0);
+
+detach:
+	udl_detach(dev);
+
+	return (ENXIO);
+}
+
+static int
+udl_detach(device_t dev)
+{
+	struct udl_softc *sc = device_get_softc(dev);
+
+	if (sc->sc_fbdev != NULL) {
+		device_t bdev;
+
+		bdev = sc->sc_fbdev;
+		sc->sc_fbdev = NULL;
+		device_detach(bdev);
+		device_delete_child(dev, bdev);
+	}
+	UDL_LOCK(sc);
+	sc->sc_gone = 1;
+	callout_stop(&sc->sc_callout);
+	UDL_UNLOCK(sc);
+
+	usbd_transfer_unsetup(sc->sc_xfer, UDL_N_TRANSFER);
+
+	callout_drain(&sc->sc_callout);
+
+	mtx_destroy(&sc->sc_mtx);
+	cv_destroy(&sc->sc_cv);
+
+	/*
+	 * Free framebuffer memory, if any.
+	 */
+	free(sc->sc_fb_addr, M_DEVBUF);
+	free(sc->sc_fb_copy, M_DEVBUF);
+
+	return (0);
+}
+
+static struct fb_info *
+udl_fb_getinfo(device_t dev)
+{
+	struct udl_softc *sc = device_get_softc(dev);
+
+	return (&sc->sc_fb_info);
+}
+
+#if 0
+static int
+udl_fb_blank_display(device_t dev, int mode)
+{
+	struct udl_softc *sc = device_get_softc(dev);
+
+	switch (mode) {
+	case V_DISPLAY_ON:
+		udl_power_save(sc, 1, M_WAITOK);
+		break;
+	case V_DISPLAY_BLANK:
+		udl_power_save(sc, 1, M_WAITOK);
+		if (sc->sc_fb_addr != 0) {
+			const uint32_t max = udl_get_fb_size(sc);
+
+			memset((void *)sc->sc_fb_addr, 0, max);
+		}
+		break;
+	case V_DISPLAY_STAND_BY:
+	case V_DISPLAY_SUSPEND:
+		udl_power_save(sc, 0, M_WAITOK);
+		break;
+	}
+	return (0);
+}
+#endif
+
+static struct udl_cmd_buf *
+udl_cmd_buf_alloc(struct udl_softc *sc, int flags)
+{
+	struct udl_cmd_buf *cb;
+
+	UDL_LOCK(sc);
+	while ((cb = TAILQ_FIRST(&sc->sc_cmd_buf_free)) == NULL) {
+		if (flags != M_WAITOK)
+			break;
+		cv_wait(&sc->sc_cv, &sc->sc_mtx);
+	}
+	if (cb != NULL) {
+		TAILQ_REMOVE(&sc->sc_cmd_buf_free, cb, entry);
+		cb->off = 0;
+	}
+	UDL_UNLOCK(sc);
+	return (cb);
+}
+
+static void
+udl_cmd_buf_send(struct udl_softc *sc, struct udl_cmd_buf *cb)
+{
+	UDL_LOCK(sc);
+	if (sc->sc_gone) {
+		TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry);
+	} else {
+		/* mark end of command stack */
+		udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
+		udl_cmd_insert_int_1(cb, UDL_BULK_CMD_EOC);
+
+		TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_pending, cb, entry);
+		usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]);
+		usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]);
+	}
+	UDL_UNLOCK(sc);
+}
+
+static struct udl_cmd_buf *
+udl_fb_synchronize(struct udl_softc *sc)
+{
+	const uint32_t max = udl_get_fb_size(sc);
+
+	while (sc->sc_sync_off < max) {
+		uint32_t delta = max - sc->sc_sync_off;
+
+		if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2)
+			delta = UDL_CMD_MAX_PIXEL_COUNT * 2;
+		if (bcmp(sc->sc_fb_addr + sc->sc_sync_off, sc->sc_fb_copy + sc->sc_sync_off, delta) != 0) {
+			struct udl_cmd_buf *cb;
+
+			cb = udl_cmd_buf_alloc(sc, M_NOWAIT);
+			if (cb == NULL)
+				goto done;
+			memcpy(sc->sc_fb_copy + sc->sc_sync_off,
+			    sc->sc_fb_addr + sc->sc_sync_off, delta);
+			udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
+			udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD);
+			udl_cmd_insert_int_3(cb, sc->sc_sync_off);
+			udl_cmd_insert_int_1(cb, delta / 2);
+			udl_cmd_insert_buf_le16(cb, sc->sc_fb_copy + sc->sc_sync_off, delta);
+			sc->sc_sync_off += delta;
+			return (cb);
+		} else {
+			sc->sc_sync_off += delta;
+		}
+	}
+done:
+	return (NULL);
+}
+
+static void
+udl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct udl_softc *sc = usbd_xfer_softc(xfer);
+	struct udl_cmd_head *phead = usbd_xfer_get_priv(xfer);
+	struct udl_cmd_buf *cb;
+	unsigned i;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry);
+	case USB_ST_SETUP:
+tr_setup:
+		for (i = 0; i != UDL_CMD_MAX_FRAMES; i++) {
+			cb = TAILQ_FIRST(&sc->sc_cmd_buf_pending);
+			if (cb == NULL) {
+				cb = udl_fb_synchronize(sc);
+				if (cb == NULL)
+					break;
+			}
+			TAILQ_REMOVE(&sc->sc_cmd_buf_pending, cb, entry);
+			TAILQ_INSERT_TAIL(phead, cb, entry);
+			usbd_xfer_set_frame_data(xfer, i, cb->buf, cb->off);
+		}
+		if (i != 0) {
+			usbd_xfer_set_frames(xfer, i);
+			usbd_transfer_submit(xfer);
+		}
+		break;
+	default:
+		TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry);
+		if (error != USB_ERR_CANCELLED) {
+			/* try clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+	/* wakeup any waiters */
+	cv_signal(&sc->sc_cv);
+}
+
+#if 0
+static int
+udl_power_save(struct udl_softc *sc, int on, int flags)
+{
+	struct udl_cmd_buf *cb;
+
+	/* get new buffer */
+	cb = udl_cmd_buf_alloc(sc, flags);
+	if (cb == NULL)
+		return (EAGAIN);
+
+	DPRINTF("screen %s\n", on ? "ON" : "OFF");
+
+	sc->sc_power_save = on ? 0 : 1;
+
+	if (on)
+		udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON);
+	else
+		udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF);
+
+	udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
+	udl_cmd_buf_send(sc, cb);
+	return (0);
+}
+#endif
+
+static int
+udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r,
+    uint16_t index, uint16_t value, uint8_t *buf, size_t len)
+{
+	usb_device_request_t req;
+	int error;
+
+	req.bmRequestType = rt;
+	req.bRequest = r;
+	USETW(req.wIndex, index);
+	USETW(req.wValue, value);
+	USETW(req.wLength, len);
+
+	error = usbd_do_request_flags(sc->sc_udev, NULL,
+	    &req, buf, 0, NULL, USB_DEFAULT_TIMEOUT);
+
+	DPRINTF("%s\n", usbd_errstr(error));
+
+	return (error);
+}
+
+static int
+udl_poll(struct udl_softc *sc, uint32_t *buf)
+{
+	uint32_t lbuf;
+	int error;
+
+	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+	    UDL_CTRL_CMD_POLL, 0x0000, 0x0000, (uint8_t *)&lbuf, sizeof(lbuf));
+	if (error == USB_ERR_NORMAL_COMPLETION)
+		*buf = le32toh(lbuf);
+	return (error);
+}
+
+static int
+udl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf)
+{
+	uint8_t lbuf[1];
+	int error;
+
+	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+	    UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1);
+	if (error == USB_ERR_NORMAL_COMPLETION)
+		*buf = *(uint8_t *)lbuf;
+	return (error);
+}
+
+static int
+udl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf)
+{
+	int error;
+
+	error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE,
+	    UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1);
+	return (error);
+}
+
+static int
+udl_read_edid(struct udl_softc *sc, uint8_t *buf)
+{
+	uint8_t lbuf[64];
+	uint16_t offset;
+	int error;
+
+	offset = 0;
+
+	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64);
+	if (error != USB_ERR_NORMAL_COMPLETION)
+		goto fail;
+	bcopy(lbuf + 1, buf + offset, 63);
+	offset += 63;
+
+	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64);
+	if (error != USB_ERR_NORMAL_COMPLETION)
+		goto fail;
+	bcopy(lbuf + 1, buf + offset, 63);
+	offset += 63;
+
+	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3);
+	if (error != USB_ERR_NORMAL_COMPLETION)
+		goto fail;
+	bcopy(lbuf + 1, buf + offset, 2);
+fail:
+	return (error);
+}
+
+static uint8_t
+udl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t hz,
+    uint16_t chip, uint32_t clock)
+{
+	uint8_t idx;
+
+	/*
+	 * Check first if we have a matching mode with pixelclock
+	 */
+	for (idx = 0; idx != UDL_MAX_MODES; idx++) {
+		if ((udl_modes[idx].hdisplay == hdisplay) &&
+		    (udl_modes[idx].vdisplay == vdisplay) &&
+		    (udl_modes[idx].clock == clock) &&
+		    (udl_modes[idx].chip <= chip)) {
+			return (idx);
+		}
+	}
+
+	/*
+	 * If not, check for matching mode with update frequency
+	 */
+	for (idx = 0; idx != UDL_MAX_MODES; idx++) {
+		if ((udl_modes[idx].hdisplay == hdisplay) &&
+		    (udl_modes[idx].vdisplay == vdisplay) &&
+		    (udl_modes[idx].hz == hz) &&
+		    (udl_modes[idx].chip <= chip)) {
+			return (idx);
+		}
+	}
+	return (idx);
+}
+
+static void
+udl_select_chip(struct udl_softc *sc, struct usb_attach_arg *uaa)
+{
+	const char *pserial;
+
+	pserial = usb_get_serial(uaa->device);
+
+	sc->sc_chip = DL120;
+
+	if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) &&
+	    (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_WSDVI)) {
+
+		/*
+		 * WS Tech DVI is DL120 or DL160. All deviced uses the
+		 * same revision (0.04) so iSerialNumber must be used
+		 * to determin which chip it is.
+		 */
+
+		if (strlen(pserial) > 7) {
+			if (strncmp(pserial, "0198-13", 7) == 0)
+				sc->sc_chip = DL160;
+		}
+		DPRINTF("iSerialNumber (%s) used to select chip (%d)\n",
+		    pserial, sc->sc_chip);
+	}
+	if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) &&
+	    (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_SWDVI)) {
+
+		/*
+		 * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision
+		 * can be used to differ between DL1x0 and DL1x5. Minor to
+		 * differ between DL1x5. iSerialNumber seems not to be uniqe.
+		 */
+
+		sc->sc_chip = DL160;
+
+		if (uaa->info.bcdDevice >= 0x100) {
+			sc->sc_chip = DL165;
+			if (uaa->info.bcdDevice == 0x104)
+				sc->sc_chip = DL195;
+			if (uaa->info.bcdDevice == 0x108)
+				sc->sc_chip = DL125;
+		}
+		DPRINTF("bcdDevice (%02x) used to select chip (%d)\n",
+		    uaa->info.bcdDevice, sc->sc_chip);
+	}
+}
+
+static int
+udl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len)
+{
+	int error;
+
+	error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE,
+	    UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len);
+	return (error);
+}
+
+static void
+udl_fbmem_alloc(struct udl_softc *sc)
+{
+	uint32_t size;
+
+	size = udl_get_fb_size(sc);
+	size = round_page(size);
+
+	sc->sc_fb_addr = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+	sc->sc_fb_copy = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+	sc->sc_fb_size = size;
+}
+
+static void
+udl_cmd_insert_int_1(struct udl_cmd_buf *cb, uint8_t value)
+{
+
+	cb->buf[cb->off] = value;
+	cb->off += 1;
+}
+
+#if 0
+static void
+udl_cmd_insert_int_2(struct udl_cmd_buf *cb, uint16_t value)
+{
+	uint16_t lvalue;
+
+	lvalue = htobe16(value);
+	bcopy(&lvalue, cb->buf + cb->off, 2);
+
+	cb->off += 2;
+}
+
+#endif
+
+static void
+udl_cmd_insert_int_3(struct udl_cmd_buf *cb, uint32_t value)
+{
+	uint32_t lvalue;
+
+#if BYTE_ORDER == BIG_ENDIAN
+	lvalue = htobe32(value) << 8;
+#else
+	lvalue = htobe32(value) >> 8;
+#endif
+	bcopy(&lvalue, cb->buf + cb->off, 3);
+
+	cb->off += 3;
+}
+
+#if 0
+static void
+udl_cmd_insert_int_4(struct udl_cmd_buf *cb, uint32_t value)
+{
+	uint32_t lvalue;
+
+	lvalue = htobe32(value);
+	bcopy(&lvalue, cb->buf + cb->off, 4);
+
+	cb->off += 4;
+}
+
+#endif
+
+static void
+udl_cmd_insert_buf_le16(struct udl_cmd_buf *cb, const uint8_t *buf, uint32_t len)
+{
+	uint32_t x;
+
+	for (x = 0; x != len; x += 2) {
+		/* byte swap from little endian to big endian */
+		cb->buf[cb->off + x + 0] = buf[x + 1];
+		cb->buf[cb->off + x + 1] = buf[x + 0];
+	}
+	cb->off += len;
+}
+
+static void
+udl_cmd_write_reg_1(struct udl_cmd_buf *cb, uint8_t reg, uint8_t val)
+{
+
+	udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
+	udl_cmd_insert_int_1(cb, UDL_BULK_CMD_REG_WRITE_1);
+	udl_cmd_insert_int_1(cb, reg);
+	udl_cmd_insert_int_1(cb, val);
+}
+
+static void
+udl_cmd_write_reg_3(struct udl_cmd_buf *cb, uint8_t reg, uint32_t val)
+{
+
+	udl_cmd_write_reg_1(cb, reg + 0, (val >> 16) & 0xff);
+	udl_cmd_write_reg_1(cb, reg + 1, (val >> 8) & 0xff);
+	udl_cmd_write_reg_1(cb, reg + 2, (val >> 0) & 0xff);
+}
+
+static int
+udl_init_chip(struct udl_softc *sc)
+{
+	uint32_t ui32;
+	uint8_t ui8;
+	int error;
+
+	error = udl_poll(sc, &ui32);
+	if (error != USB_ERR_NORMAL_COMPLETION)
+		return (error);
+	DPRINTF("poll=0x%08x\n", ui32);
+
+	/* Some products may use later chip too */
+	switch (ui32 & 0xff) {
+	case 0xf1:			/* DL1x5 */
+		switch (sc->sc_chip) {
+		case DL120:
+			sc->sc_chip = DL125;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201502151202.t1FC2IcA099300>