xref: /freebsd/sys/dev/usb/video/udl.c (revision 0867995ca0720787373ab219607d4a11b15eb845)
1*0867995cSHans Petter Selasky /*	$OpenBSD: udl.c,v 1.81 2014/12/09 07:05:06 doug Exp $ */
2*0867995cSHans Petter Selasky /*	$FreeBSD$ */
3*0867995cSHans Petter Selasky 
4*0867995cSHans Petter Selasky /*-
5*0867995cSHans Petter Selasky  * Copyright (c) 2015 Hans Petter Selasky <hselasky@freebsd.org>
6*0867995cSHans Petter Selasky  * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org>
7*0867995cSHans Petter Selasky  *
8*0867995cSHans Petter Selasky  * Permission to use, copy, modify, and distribute this software for any
9*0867995cSHans Petter Selasky  * purpose with or without fee is hereby granted, provided that the above
10*0867995cSHans Petter Selasky  * copyright notice and this permission notice appear in all copies.
11*0867995cSHans Petter Selasky  *
12*0867995cSHans Petter Selasky  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13*0867995cSHans Petter Selasky  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14*0867995cSHans Petter Selasky  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15*0867995cSHans Petter Selasky  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16*0867995cSHans Petter Selasky  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17*0867995cSHans Petter Selasky  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18*0867995cSHans Petter Selasky  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19*0867995cSHans Petter Selasky  */
20*0867995cSHans Petter Selasky 
21*0867995cSHans Petter Selasky /*
22*0867995cSHans Petter Selasky  * Driver for the "DisplayLink DL-120 / DL-160" graphic chips based on
23*0867995cSHans Petter Selasky  * the reversed engineered specifications of Florian Echtler
24*0867995cSHans Petter Selasky  * <floe@butterbrot.org>:
25*0867995cSHans Petter Selasky  *
26*0867995cSHans Petter Selasky  * 	http://floe.butterbrot.org/displaylink/doku.php
27*0867995cSHans Petter Selasky  */
28*0867995cSHans Petter Selasky 
29*0867995cSHans Petter Selasky #include <sys/param.h>
30*0867995cSHans Petter Selasky #include <sys/bus.h>
31*0867995cSHans Petter Selasky #include <sys/callout.h>
32*0867995cSHans Petter Selasky #include <sys/conf.h>
33*0867995cSHans Petter Selasky #include <sys/kernel.h>
34*0867995cSHans Petter Selasky #include <sys/lock.h>
35*0867995cSHans Petter Selasky #include <sys/module.h>
36*0867995cSHans Petter Selasky #include <sys/mutex.h>
37*0867995cSHans Petter Selasky #include <sys/condvar.h>
38*0867995cSHans Petter Selasky #include <sys/sysctl.h>
39*0867995cSHans Petter Selasky #include <sys/systm.h>
40*0867995cSHans Petter Selasky #include <sys/consio.h>
41*0867995cSHans Petter Selasky #include <sys/fbio.h>
42*0867995cSHans Petter Selasky 
43*0867995cSHans Petter Selasky #include <dev/fb/fbreg.h>
44*0867995cSHans Petter Selasky #include <dev/syscons/syscons.h>
45*0867995cSHans Petter Selasky 
46*0867995cSHans Petter Selasky #include <dev/videomode/videomode.h>
47*0867995cSHans Petter Selasky #include <dev/videomode/edidvar.h>
48*0867995cSHans Petter Selasky 
49*0867995cSHans Petter Selasky #include <dev/usb/usb.h>
50*0867995cSHans Petter Selasky #include <dev/usb/usbdi.h>
51*0867995cSHans Petter Selasky #include <dev/usb/usbdi_util.h>
52*0867995cSHans Petter Selasky #include "usbdevs.h"
53*0867995cSHans Petter Selasky 
54*0867995cSHans Petter Selasky #include <dev/usb/video/udl.h>
55*0867995cSHans Petter Selasky 
56*0867995cSHans Petter Selasky #include "fb_if.h"
57*0867995cSHans Petter Selasky 
58*0867995cSHans Petter Selasky #undef DPRINTF
59*0867995cSHans Petter Selasky #undef DPRINTFN
60*0867995cSHans Petter Selasky #define	USB_DEBUG_VAR udl_debug
61*0867995cSHans Petter Selasky #include <dev/usb/usb_debug.h>
62*0867995cSHans Petter Selasky 
63*0867995cSHans Petter Selasky #ifdef USB_DEBUG
64*0867995cSHans Petter Selasky static int udl_debug = 0;
65*0867995cSHans Petter Selasky 
66*0867995cSHans Petter Selasky static	SYSCTL_NODE(_hw_usb, OID_AUTO, udl, CTLFLAG_RW, 0, "USB UDL");
67*0867995cSHans Petter Selasky 
68*0867995cSHans Petter Selasky SYSCTL_INT(_hw_usb_udl, OID_AUTO, debug, CTLFLAG_RWTUN,
69*0867995cSHans Petter Selasky     &udl_debug, 0, "Debug level");
70*0867995cSHans Petter Selasky #endif
71*0867995cSHans Petter Selasky 
72*0867995cSHans Petter Selasky /*
73*0867995cSHans Petter Selasky  * Prototypes.
74*0867995cSHans Petter Selasky  */
75*0867995cSHans Petter Selasky static usb_callback_t udl_bulk_write_callback;
76*0867995cSHans Petter Selasky 
77*0867995cSHans Petter Selasky static device_probe_t udl_probe;
78*0867995cSHans Petter Selasky static device_attach_t udl_attach;
79*0867995cSHans Petter Selasky static device_detach_t udl_detach;
80*0867995cSHans Petter Selasky static fb_getinfo_t udl_fb_getinfo;
81*0867995cSHans Petter Selasky #if 0
82*0867995cSHans Petter Selasky static fb_blank_display_t udl_fb_blank_display;
83*0867995cSHans Petter Selasky #endif
84*0867995cSHans Petter Selasky 
85*0867995cSHans Petter Selasky static void udl_select_chip(struct udl_softc *, struct usb_attach_arg *);
86*0867995cSHans Petter Selasky static int udl_init_chip(struct udl_softc *);
87*0867995cSHans Petter Selasky static void udl_select_mode(struct udl_softc *);
88*0867995cSHans Petter Selasky static int udl_init_resolution(struct udl_softc *);
89*0867995cSHans Petter Selasky static void udl_fbmem_alloc(struct udl_softc *);
90*0867995cSHans Petter Selasky static int udl_cmd_write_buf_le16(struct udl_softc *, const uint8_t *, uint32_t, uint8_t, int);
91*0867995cSHans Petter Selasky static int udl_cmd_buf_copy_le16(struct udl_softc *, uint32_t, uint32_t, uint8_t, int);
92*0867995cSHans Petter Selasky static void udl_cmd_insert_int_1(struct udl_cmd_buf *, uint8_t);
93*0867995cSHans Petter Selasky static void udl_cmd_insert_int_3(struct udl_cmd_buf *, uint32_t);
94*0867995cSHans Petter Selasky static void udl_cmd_insert_buf_le16(struct udl_cmd_buf *, const uint8_t *, uint32_t);
95*0867995cSHans Petter Selasky static void udl_cmd_write_reg_1(struct udl_cmd_buf *, uint8_t, uint8_t);
96*0867995cSHans Petter Selasky static void udl_cmd_write_reg_3(struct udl_cmd_buf *, uint8_t, uint32_t);
97*0867995cSHans Petter Selasky #if 0
98*0867995cSHans Petter Selasky static int udl_power_save(struct udl_softc *, int, int);
99*0867995cSHans Petter Selasky #endif
100*0867995cSHans Petter Selasky 
101*0867995cSHans Petter Selasky static const struct usb_config udl_config[UDL_N_TRANSFER] = {
102*0867995cSHans Petter Selasky 	[UDL_BULK_WRITE_0] = {
103*0867995cSHans Petter Selasky 		.type = UE_BULK,
104*0867995cSHans Petter Selasky 		.endpoint = UE_ADDR_ANY,
105*0867995cSHans Petter Selasky 		.direction = UE_DIR_TX,
106*0867995cSHans Petter Selasky 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
107*0867995cSHans Petter Selasky 		.bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES,
108*0867995cSHans Petter Selasky 		.callback = &udl_bulk_write_callback,
109*0867995cSHans Petter Selasky 		.frames = UDL_CMD_MAX_FRAMES,
110*0867995cSHans Petter Selasky 		.timeout = 5000,	/* 5 seconds */
111*0867995cSHans Petter Selasky 	},
112*0867995cSHans Petter Selasky 	[UDL_BULK_WRITE_1] = {
113*0867995cSHans Petter Selasky 		.type = UE_BULK,
114*0867995cSHans Petter Selasky 		.endpoint = UE_ADDR_ANY,
115*0867995cSHans Petter Selasky 		.direction = UE_DIR_TX,
116*0867995cSHans Petter Selasky 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
117*0867995cSHans Petter Selasky 		.bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES,
118*0867995cSHans Petter Selasky 		.callback = &udl_bulk_write_callback,
119*0867995cSHans Petter Selasky 		.frames = UDL_CMD_MAX_FRAMES,
120*0867995cSHans Petter Selasky 		.timeout = 5000,	/* 5 seconds */
121*0867995cSHans Petter Selasky 	},
122*0867995cSHans Petter Selasky };
123*0867995cSHans Petter Selasky 
124*0867995cSHans Petter Selasky /*
125*0867995cSHans Petter Selasky  * Driver glue.
126*0867995cSHans Petter Selasky  */
127*0867995cSHans Petter Selasky static devclass_t udl_devclass;
128*0867995cSHans Petter Selasky 
129*0867995cSHans Petter Selasky static device_method_t udl_methods[] = {
130*0867995cSHans Petter Selasky 	DEVMETHOD(device_probe, udl_probe),
131*0867995cSHans Petter Selasky 	DEVMETHOD(device_attach, udl_attach),
132*0867995cSHans Petter Selasky 	DEVMETHOD(device_detach, udl_detach),
133*0867995cSHans Petter Selasky 	DEVMETHOD(fb_getinfo, udl_fb_getinfo),
134*0867995cSHans Petter Selasky #if 0
135*0867995cSHans Petter Selasky 	DEVMETHOD(fb_blank_display, udl_fb_blank_display),
136*0867995cSHans Petter Selasky #endif
137*0867995cSHans Petter Selasky 	DEVMETHOD_END
138*0867995cSHans Petter Selasky };
139*0867995cSHans Petter Selasky 
140*0867995cSHans Petter Selasky static driver_t udl_driver = {
141*0867995cSHans Petter Selasky 	.name = "udl",
142*0867995cSHans Petter Selasky 	.methods = udl_methods,
143*0867995cSHans Petter Selasky 	.size = sizeof(struct udl_softc),
144*0867995cSHans Petter Selasky };
145*0867995cSHans Petter Selasky 
146*0867995cSHans Petter Selasky DRIVER_MODULE(udl, uhub, udl_driver, udl_devclass, NULL, NULL);
147*0867995cSHans Petter Selasky MODULE_DEPEND(udl, usb, 1, 1, 1);
148*0867995cSHans Petter Selasky MODULE_DEPEND(udl, fbd, 1, 1, 1);
149*0867995cSHans Petter Selasky MODULE_DEPEND(udl, videomode, 1, 1, 1);
150*0867995cSHans Petter Selasky MODULE_VERSION(udl, 1);
151*0867995cSHans Petter Selasky 
152*0867995cSHans Petter Selasky /*
153*0867995cSHans Petter Selasky  * Matching devices.
154*0867995cSHans Petter Selasky  */
155*0867995cSHans Petter Selasky static const STRUCT_USB_HOST_ID udl_devs[] = {
156*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U, DL120)},
157*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U, DL120)},
158*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020, DL160)},
159*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220, DL165)},
160*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60, DL160)},
161*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI, DL160)},
162*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10, DL120)},
163*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI, DLUNK)},
164*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008, DL160)},
165*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK, DL160)},
166*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571, DL160)},
167*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061, DL195)},
168*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK, DL165)},
169*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI, DLUNK)},
170*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0, DL120)},
171*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV, DL160)},
172*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70, DL125)},
173*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2, DLUNK)},
174*0867995cSHans Petter Selasky 	{USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)}
175*0867995cSHans Petter Selasky };
176*0867995cSHans Petter Selasky 
177*0867995cSHans Petter Selasky static uint32_t
178*0867995cSHans Petter Selasky udl_get_fb_size(struct udl_softc *sc)
179*0867995cSHans Petter Selasky {
180*0867995cSHans Petter Selasky 	unsigned i = sc->sc_cur_mode;
181*0867995cSHans Petter Selasky 
182*0867995cSHans Petter Selasky 	return ((uint32_t)udl_modes[i].hdisplay *
183*0867995cSHans Petter Selasky 	    (uint32_t)udl_modes[i].vdisplay * 2);
184*0867995cSHans Petter Selasky }
185*0867995cSHans Petter Selasky 
186*0867995cSHans Petter Selasky static uint32_t
187*0867995cSHans Petter Selasky udl_get_fb_width(struct udl_softc *sc)
188*0867995cSHans Petter Selasky {
189*0867995cSHans Petter Selasky 	unsigned i = sc->sc_cur_mode;
190*0867995cSHans Petter Selasky 
191*0867995cSHans Petter Selasky 	return (udl_modes[i].vdisplay);
192*0867995cSHans Petter Selasky }
193*0867995cSHans Petter Selasky 
194*0867995cSHans Petter Selasky static uint32_t
195*0867995cSHans Petter Selasky udl_get_fb_height(struct udl_softc *sc)
196*0867995cSHans Petter Selasky {
197*0867995cSHans Petter Selasky 	unsigned i = sc->sc_cur_mode;
198*0867995cSHans Petter Selasky 
199*0867995cSHans Petter Selasky 	return (udl_modes[i].hdisplay);
200*0867995cSHans Petter Selasky }
201*0867995cSHans Petter Selasky 
202*0867995cSHans Petter Selasky static uint32_t
203*0867995cSHans Petter Selasky udl_get_fb_hz(struct udl_softc *sc)
204*0867995cSHans Petter Selasky {
205*0867995cSHans Petter Selasky 	unsigned i = sc->sc_cur_mode;
206*0867995cSHans Petter Selasky 
207*0867995cSHans Petter Selasky 	return (udl_modes[i].hz);
208*0867995cSHans Petter Selasky }
209*0867995cSHans Petter Selasky 
210*0867995cSHans Petter Selasky static void
211*0867995cSHans Petter Selasky udl_callout(void *arg)
212*0867995cSHans Petter Selasky {
213*0867995cSHans Petter Selasky 	struct udl_softc *sc = arg;
214*0867995cSHans Petter Selasky 	const uint32_t max = udl_get_fb_size(sc);
215*0867995cSHans Petter Selasky 
216*0867995cSHans Petter Selasky 	if (sc->sc_power_save == 0) {
217*0867995cSHans Petter Selasky 		if (sc->sc_sync_off >= max)
218*0867995cSHans Petter Selasky 			sc->sc_sync_off = 0;
219*0867995cSHans Petter Selasky 		usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]);
220*0867995cSHans Petter Selasky 		usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]);
221*0867995cSHans Petter Selasky 	}
222*0867995cSHans Petter Selasky 	callout_reset(&sc->sc_callout, hz / 5, &udl_callout, sc);
223*0867995cSHans Petter Selasky }
224*0867995cSHans Petter Selasky 
225*0867995cSHans Petter Selasky static int
226*0867995cSHans Petter Selasky udl_probe(device_t dev)
227*0867995cSHans Petter Selasky {
228*0867995cSHans Petter Selasky 	struct usb_attach_arg *uaa = device_get_ivars(dev);
229*0867995cSHans Petter Selasky 
230*0867995cSHans Petter Selasky 	if (uaa->usb_mode != USB_MODE_HOST)
231*0867995cSHans Petter Selasky 		return (ENXIO);
232*0867995cSHans Petter Selasky 	if (uaa->info.bConfigIndex != 0)
233*0867995cSHans Petter Selasky 		return (ENXIO);
234*0867995cSHans Petter Selasky 	if (uaa->info.bIfaceIndex != 0)
235*0867995cSHans Petter Selasky 		return (ENXIO);
236*0867995cSHans Petter Selasky 
237*0867995cSHans Petter Selasky 	return (usbd_lookup_id_by_uaa(udl_devs, sizeof(udl_devs), uaa));
238*0867995cSHans Petter Selasky }
239*0867995cSHans Petter Selasky 
240*0867995cSHans Petter Selasky static int
241*0867995cSHans Petter Selasky udl_attach(device_t dev)
242*0867995cSHans Petter Selasky {
243*0867995cSHans Petter Selasky 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
244*0867995cSHans Petter Selasky 	struct sysctl_oid *tree = device_get_sysctl_tree(dev);
245*0867995cSHans Petter Selasky 	struct udl_softc *sc = device_get_softc(dev);
246*0867995cSHans Petter Selasky 	struct usb_attach_arg *uaa = device_get_ivars(dev);
247*0867995cSHans Petter Selasky 	int error;
248*0867995cSHans Petter Selasky 	int i;
249*0867995cSHans Petter Selasky 
250*0867995cSHans Petter Selasky 	device_set_usb_desc(dev);
251*0867995cSHans Petter Selasky 
252*0867995cSHans Petter Selasky 	mtx_init(&sc->sc_mtx, "UDL lock", NULL, MTX_DEF);
253*0867995cSHans Petter Selasky 	cv_init(&sc->sc_cv, "UDLCV");
254*0867995cSHans Petter Selasky 	callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
255*0867995cSHans Petter Selasky 	sc->sc_udev = uaa->device;
256*0867995cSHans Petter Selasky 
257*0867995cSHans Petter Selasky 	error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
258*0867995cSHans Petter Selasky 	    sc->sc_xfer, udl_config, UDL_N_TRANSFER, sc, &sc->sc_mtx);
259*0867995cSHans Petter Selasky 
260*0867995cSHans Petter Selasky 	if (error) {
261*0867995cSHans Petter Selasky 		DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error));
262*0867995cSHans Petter Selasky 		goto detach;
263*0867995cSHans Petter Selasky 	}
264*0867995cSHans Petter Selasky 	usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_0], &sc->sc_xfer_head[0]);
265*0867995cSHans Petter Selasky 	usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_1], &sc->sc_xfer_head[1]);
266*0867995cSHans Petter Selasky 
267*0867995cSHans Petter Selasky 	TAILQ_INIT(&sc->sc_xfer_head[0]);
268*0867995cSHans Petter Selasky 	TAILQ_INIT(&sc->sc_xfer_head[1]);
269*0867995cSHans Petter Selasky 	TAILQ_INIT(&sc->sc_cmd_buf_free);
270*0867995cSHans Petter Selasky 	TAILQ_INIT(&sc->sc_cmd_buf_pending);
271*0867995cSHans Petter Selasky 
272*0867995cSHans Petter Selasky 	sc->sc_def_chip = -1;
273*0867995cSHans Petter Selasky 	sc->sc_chip = USB_GET_DRIVER_INFO(uaa);
274*0867995cSHans Petter Selasky 	sc->sc_def_mode = -1;
275*0867995cSHans Petter Selasky 	sc->sc_cur_mode = UDL_MAX_MODES;
276*0867995cSHans Petter Selasky 
277*0867995cSHans Petter Selasky 	/* Allow chip ID to be overwritten */
278*0867995cSHans Petter Selasky 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid_force",
279*0867995cSHans Petter Selasky 	    CTLFLAG_RWTUN, &sc->sc_def_chip, 0, "chip ID");
280*0867995cSHans Petter Selasky 
281*0867995cSHans Petter Selasky 	/* Export current chip ID */
282*0867995cSHans Petter Selasky 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid",
283*0867995cSHans Petter Selasky 	    CTLFLAG_RD, &sc->sc_chip, 0, "chip ID");
284*0867995cSHans Petter Selasky 
285*0867995cSHans Petter Selasky 	if (sc->sc_def_chip > -1 && sc->sc_def_chip <= DLMAX) {
286*0867995cSHans Petter Selasky 		device_printf(dev, "Forcing chip ID to 0x%04x\n", sc->sc_def_chip);
287*0867995cSHans Petter Selasky 		sc->sc_chip = sc->sc_def_chip;
288*0867995cSHans Petter Selasky 	}
289*0867995cSHans Petter Selasky 	/*
290*0867995cSHans Petter Selasky 	 * The product might have more than one chip
291*0867995cSHans Petter Selasky 	 */
292*0867995cSHans Petter Selasky 	if (sc->sc_chip == DLUNK)
293*0867995cSHans Petter Selasky 		udl_select_chip(sc, uaa);
294*0867995cSHans Petter Selasky 
295*0867995cSHans Petter Selasky 	for (i = 0; i != UDL_CMD_MAX_BUFFERS; i++) {
296*0867995cSHans Petter Selasky 		struct udl_cmd_buf *cb = &sc->sc_cmd_buf_temp[i];
297*0867995cSHans Petter Selasky 
298*0867995cSHans Petter Selasky 		TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry);
299*0867995cSHans Petter Selasky 	}
300*0867995cSHans Petter Selasky 
301*0867995cSHans Petter Selasky 	/*
302*0867995cSHans Petter Selasky 	 * Initialize chip.
303*0867995cSHans Petter Selasky 	 */
304*0867995cSHans Petter Selasky 	error = udl_init_chip(sc);
305*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
306*0867995cSHans Petter Selasky 		goto detach;
307*0867995cSHans Petter Selasky 
308*0867995cSHans Petter Selasky 	/*
309*0867995cSHans Petter Selasky 	 * Select edid mode.
310*0867995cSHans Petter Selasky 	 */
311*0867995cSHans Petter Selasky 	udl_select_mode(sc);
312*0867995cSHans Petter Selasky 
313*0867995cSHans Petter Selasky 	/* Allow default mode to be overwritten */
314*0867995cSHans Petter Selasky 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode_force",
315*0867995cSHans Petter Selasky 	    CTLFLAG_RWTUN, &sc->sc_def_mode, 0, "mode");
316*0867995cSHans Petter Selasky 
317*0867995cSHans Petter Selasky 	/* Export current mode */
318*0867995cSHans Petter Selasky 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode",
319*0867995cSHans Petter Selasky 	    CTLFLAG_RD, &sc->sc_cur_mode, 0, "mode");
320*0867995cSHans Petter Selasky 
321*0867995cSHans Petter Selasky 	i = sc->sc_def_mode;
322*0867995cSHans Petter Selasky 	if (i > -1 && i < UDL_MAX_MODES) {
323*0867995cSHans Petter Selasky 		if (udl_modes[i].chip <= sc->sc_chip) {
324*0867995cSHans Petter Selasky 			device_printf(dev, "Forcing mode to %d\n", i);
325*0867995cSHans Petter Selasky 			sc->sc_cur_mode = i;
326*0867995cSHans Petter Selasky 		}
327*0867995cSHans Petter Selasky 	}
328*0867995cSHans Petter Selasky 	/* Printout current mode */
329*0867995cSHans Petter Selasky 	device_printf(dev, "Mode selected %dx%d @ %dHz\n",
330*0867995cSHans Petter Selasky 	    (int)udl_get_fb_width(sc),
331*0867995cSHans Petter Selasky 	    (int)udl_get_fb_height(sc),
332*0867995cSHans Petter Selasky 	    (int)udl_get_fb_hz(sc));
333*0867995cSHans Petter Selasky 
334*0867995cSHans Petter Selasky 	udl_init_resolution(sc);
335*0867995cSHans Petter Selasky 
336*0867995cSHans Petter Selasky 	/* Allocate frame buffer */
337*0867995cSHans Petter Selasky 	udl_fbmem_alloc(sc);
338*0867995cSHans Petter Selasky 
339*0867995cSHans Petter Selasky 	UDL_LOCK(sc);
340*0867995cSHans Petter Selasky 	udl_callout(sc);
341*0867995cSHans Petter Selasky 	UDL_UNLOCK(sc);
342*0867995cSHans Petter Selasky 
343*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_name = device_get_nameunit(dev);
344*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_size = sc->sc_fb_size;
345*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_bpp = 16;
346*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_depth = 16;
347*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_width = udl_get_fb_width(sc);
348*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_height = udl_get_fb_height(sc);
349*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_stride = sc->sc_fb_info.fb_width * 2;
350*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_pbase = (uintptr_t)sc->sc_fb_addr;
351*0867995cSHans Petter Selasky 	sc->sc_fb_info.fb_vbase = (uintptr_t)sc->sc_fb_addr;
352*0867995cSHans Petter Selasky 
353*0867995cSHans Petter Selasky 	sc->sc_fbdev = device_add_child(dev, "fbd", -1);
354*0867995cSHans Petter Selasky 	if (sc->sc_fbdev == NULL)
355*0867995cSHans Petter Selasky 		goto detach;
356*0867995cSHans Petter Selasky 	if (device_probe_and_attach(sc->sc_fbdev) != 0)
357*0867995cSHans Petter Selasky 		goto detach;
358*0867995cSHans Petter Selasky 
359*0867995cSHans Petter Selasky 	return (0);
360*0867995cSHans Petter Selasky 
361*0867995cSHans Petter Selasky detach:
362*0867995cSHans Petter Selasky 	udl_detach(dev);
363*0867995cSHans Petter Selasky 
364*0867995cSHans Petter Selasky 	return (ENXIO);
365*0867995cSHans Petter Selasky }
366*0867995cSHans Petter Selasky 
367*0867995cSHans Petter Selasky static int
368*0867995cSHans Petter Selasky udl_detach(device_t dev)
369*0867995cSHans Petter Selasky {
370*0867995cSHans Petter Selasky 	struct udl_softc *sc = device_get_softc(dev);
371*0867995cSHans Petter Selasky 
372*0867995cSHans Petter Selasky 	if (sc->sc_fbdev != NULL) {
373*0867995cSHans Petter Selasky 		device_t bdev;
374*0867995cSHans Petter Selasky 
375*0867995cSHans Petter Selasky 		bdev = sc->sc_fbdev;
376*0867995cSHans Petter Selasky 		sc->sc_fbdev = NULL;
377*0867995cSHans Petter Selasky 		device_detach(bdev);
378*0867995cSHans Petter Selasky 		device_delete_child(dev, bdev);
379*0867995cSHans Petter Selasky 	}
380*0867995cSHans Petter Selasky 	UDL_LOCK(sc);
381*0867995cSHans Petter Selasky 	sc->sc_gone = 1;
382*0867995cSHans Petter Selasky 	callout_stop(&sc->sc_callout);
383*0867995cSHans Petter Selasky 	UDL_UNLOCK(sc);
384*0867995cSHans Petter Selasky 
385*0867995cSHans Petter Selasky 	usbd_transfer_unsetup(sc->sc_xfer, UDL_N_TRANSFER);
386*0867995cSHans Petter Selasky 
387*0867995cSHans Petter Selasky 	callout_drain(&sc->sc_callout);
388*0867995cSHans Petter Selasky 
389*0867995cSHans Petter Selasky 	mtx_destroy(&sc->sc_mtx);
390*0867995cSHans Petter Selasky 	cv_destroy(&sc->sc_cv);
391*0867995cSHans Petter Selasky 
392*0867995cSHans Petter Selasky 	/*
393*0867995cSHans Petter Selasky 	 * Free framebuffer memory, if any.
394*0867995cSHans Petter Selasky 	 */
395*0867995cSHans Petter Selasky 	free(sc->sc_fb_addr, M_DEVBUF);
396*0867995cSHans Petter Selasky 	free(sc->sc_fb_copy, M_DEVBUF);
397*0867995cSHans Petter Selasky 
398*0867995cSHans Petter Selasky 	return (0);
399*0867995cSHans Petter Selasky }
400*0867995cSHans Petter Selasky 
401*0867995cSHans Petter Selasky static struct fb_info *
402*0867995cSHans Petter Selasky udl_fb_getinfo(device_t dev)
403*0867995cSHans Petter Selasky {
404*0867995cSHans Petter Selasky 	struct udl_softc *sc = device_get_softc(dev);
405*0867995cSHans Petter Selasky 
406*0867995cSHans Petter Selasky 	return (&sc->sc_fb_info);
407*0867995cSHans Petter Selasky }
408*0867995cSHans Petter Selasky 
409*0867995cSHans Petter Selasky #if 0
410*0867995cSHans Petter Selasky static int
411*0867995cSHans Petter Selasky udl_fb_blank_display(device_t dev, int mode)
412*0867995cSHans Petter Selasky {
413*0867995cSHans Petter Selasky 	struct udl_softc *sc = device_get_softc(dev);
414*0867995cSHans Petter Selasky 
415*0867995cSHans Petter Selasky 	switch (mode) {
416*0867995cSHans Petter Selasky 	case V_DISPLAY_ON:
417*0867995cSHans Petter Selasky 		udl_power_save(sc, 1, M_WAITOK);
418*0867995cSHans Petter Selasky 		break;
419*0867995cSHans Petter Selasky 	case V_DISPLAY_BLANK:
420*0867995cSHans Petter Selasky 		udl_power_save(sc, 1, M_WAITOK);
421*0867995cSHans Petter Selasky 		if (sc->sc_fb_addr != 0) {
422*0867995cSHans Petter Selasky 			const uint32_t max = udl_get_fb_size(sc);
423*0867995cSHans Petter Selasky 
424*0867995cSHans Petter Selasky 			memset((void *)sc->sc_fb_addr, 0, max);
425*0867995cSHans Petter Selasky 		}
426*0867995cSHans Petter Selasky 		break;
427*0867995cSHans Petter Selasky 	case V_DISPLAY_STAND_BY:
428*0867995cSHans Petter Selasky 	case V_DISPLAY_SUSPEND:
429*0867995cSHans Petter Selasky 		udl_power_save(sc, 0, M_WAITOK);
430*0867995cSHans Petter Selasky 		break;
431*0867995cSHans Petter Selasky 	}
432*0867995cSHans Petter Selasky 	return (0);
433*0867995cSHans Petter Selasky }
434*0867995cSHans Petter Selasky #endif
435*0867995cSHans Petter Selasky 
436*0867995cSHans Petter Selasky static struct udl_cmd_buf *
437*0867995cSHans Petter Selasky udl_cmd_buf_alloc(struct udl_softc *sc, int flags)
438*0867995cSHans Petter Selasky {
439*0867995cSHans Petter Selasky 	struct udl_cmd_buf *cb;
440*0867995cSHans Petter Selasky 
441*0867995cSHans Petter Selasky 	UDL_LOCK(sc);
442*0867995cSHans Petter Selasky 	while ((cb = TAILQ_FIRST(&sc->sc_cmd_buf_free)) == NULL) {
443*0867995cSHans Petter Selasky 		if (flags != M_WAITOK)
444*0867995cSHans Petter Selasky 			break;
445*0867995cSHans Petter Selasky 		cv_wait(&sc->sc_cv, &sc->sc_mtx);
446*0867995cSHans Petter Selasky 	}
447*0867995cSHans Petter Selasky 	if (cb != NULL) {
448*0867995cSHans Petter Selasky 		TAILQ_REMOVE(&sc->sc_cmd_buf_free, cb, entry);
449*0867995cSHans Petter Selasky 		cb->off = 0;
450*0867995cSHans Petter Selasky 	}
451*0867995cSHans Petter Selasky 	UDL_UNLOCK(sc);
452*0867995cSHans Petter Selasky 	return (cb);
453*0867995cSHans Petter Selasky }
454*0867995cSHans Petter Selasky 
455*0867995cSHans Petter Selasky static void
456*0867995cSHans Petter Selasky udl_cmd_buf_send(struct udl_softc *sc, struct udl_cmd_buf *cb)
457*0867995cSHans Petter Selasky {
458*0867995cSHans Petter Selasky 	UDL_LOCK(sc);
459*0867995cSHans Petter Selasky 	if (sc->sc_gone) {
460*0867995cSHans Petter Selasky 		TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry);
461*0867995cSHans Petter Selasky 	} else {
462*0867995cSHans Petter Selasky 		/* mark end of command stack */
463*0867995cSHans Petter Selasky 		udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
464*0867995cSHans Petter Selasky 		udl_cmd_insert_int_1(cb, UDL_BULK_CMD_EOC);
465*0867995cSHans Petter Selasky 
466*0867995cSHans Petter Selasky 		TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_pending, cb, entry);
467*0867995cSHans Petter Selasky 		usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]);
468*0867995cSHans Petter Selasky 		usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]);
469*0867995cSHans Petter Selasky 	}
470*0867995cSHans Petter Selasky 	UDL_UNLOCK(sc);
471*0867995cSHans Petter Selasky }
472*0867995cSHans Petter Selasky 
473*0867995cSHans Petter Selasky static struct udl_cmd_buf *
474*0867995cSHans Petter Selasky udl_fb_synchronize(struct udl_softc *sc)
475*0867995cSHans Petter Selasky {
476*0867995cSHans Petter Selasky 	const uint32_t max = udl_get_fb_size(sc);
477*0867995cSHans Petter Selasky 
478*0867995cSHans Petter Selasky 	while (sc->sc_sync_off < max) {
479*0867995cSHans Petter Selasky 		uint32_t delta = max - sc->sc_sync_off;
480*0867995cSHans Petter Selasky 
481*0867995cSHans Petter Selasky 		if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2)
482*0867995cSHans Petter Selasky 			delta = UDL_CMD_MAX_PIXEL_COUNT * 2;
483*0867995cSHans Petter Selasky 		if (bcmp(sc->sc_fb_addr + sc->sc_sync_off, sc->sc_fb_copy + sc->sc_sync_off, delta) != 0) {
484*0867995cSHans Petter Selasky 			struct udl_cmd_buf *cb;
485*0867995cSHans Petter Selasky 
486*0867995cSHans Petter Selasky 			cb = udl_cmd_buf_alloc(sc, M_NOWAIT);
487*0867995cSHans Petter Selasky 			if (cb == NULL)
488*0867995cSHans Petter Selasky 				goto done;
489*0867995cSHans Petter Selasky 			memcpy(sc->sc_fb_copy + sc->sc_sync_off,
490*0867995cSHans Petter Selasky 			    sc->sc_fb_addr + sc->sc_sync_off, delta);
491*0867995cSHans Petter Selasky 			udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
492*0867995cSHans Petter Selasky 			udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD);
493*0867995cSHans Petter Selasky 			udl_cmd_insert_int_3(cb, sc->sc_sync_off);
494*0867995cSHans Petter Selasky 			udl_cmd_insert_int_1(cb, delta / 2);
495*0867995cSHans Petter Selasky 			udl_cmd_insert_buf_le16(cb, sc->sc_fb_copy + sc->sc_sync_off, delta);
496*0867995cSHans Petter Selasky 			sc->sc_sync_off += delta;
497*0867995cSHans Petter Selasky 			return (cb);
498*0867995cSHans Petter Selasky 		} else {
499*0867995cSHans Petter Selasky 			sc->sc_sync_off += delta;
500*0867995cSHans Petter Selasky 		}
501*0867995cSHans Petter Selasky 	}
502*0867995cSHans Petter Selasky done:
503*0867995cSHans Petter Selasky 	return (NULL);
504*0867995cSHans Petter Selasky }
505*0867995cSHans Petter Selasky 
506*0867995cSHans Petter Selasky static void
507*0867995cSHans Petter Selasky udl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
508*0867995cSHans Petter Selasky {
509*0867995cSHans Petter Selasky 	struct udl_softc *sc = usbd_xfer_softc(xfer);
510*0867995cSHans Petter Selasky 	struct udl_cmd_head *phead = usbd_xfer_get_priv(xfer);
511*0867995cSHans Petter Selasky 	struct udl_cmd_buf *cb;
512*0867995cSHans Petter Selasky 	unsigned i;
513*0867995cSHans Petter Selasky 
514*0867995cSHans Petter Selasky 	switch (USB_GET_STATE(xfer)) {
515*0867995cSHans Petter Selasky 	case USB_ST_TRANSFERRED:
516*0867995cSHans Petter Selasky 		TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry);
517*0867995cSHans Petter Selasky 	case USB_ST_SETUP:
518*0867995cSHans Petter Selasky tr_setup:
519*0867995cSHans Petter Selasky 		for (i = 0; i != UDL_CMD_MAX_FRAMES; i++) {
520*0867995cSHans Petter Selasky 			cb = TAILQ_FIRST(&sc->sc_cmd_buf_pending);
521*0867995cSHans Petter Selasky 			if (cb == NULL) {
522*0867995cSHans Petter Selasky 				cb = udl_fb_synchronize(sc);
523*0867995cSHans Petter Selasky 				if (cb == NULL)
524*0867995cSHans Petter Selasky 					break;
525*0867995cSHans Petter Selasky 			}
526*0867995cSHans Petter Selasky 			TAILQ_REMOVE(&sc->sc_cmd_buf_pending, cb, entry);
527*0867995cSHans Petter Selasky 			TAILQ_INSERT_TAIL(phead, cb, entry);
528*0867995cSHans Petter Selasky 			usbd_xfer_set_frame_data(xfer, i, cb->buf, cb->off);
529*0867995cSHans Petter Selasky 		}
530*0867995cSHans Petter Selasky 		if (i != 0) {
531*0867995cSHans Petter Selasky 			usbd_xfer_set_frames(xfer, i);
532*0867995cSHans Petter Selasky 			usbd_transfer_submit(xfer);
533*0867995cSHans Petter Selasky 		}
534*0867995cSHans Petter Selasky 		break;
535*0867995cSHans Petter Selasky 	default:
536*0867995cSHans Petter Selasky 		TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry);
537*0867995cSHans Petter Selasky 		if (error != USB_ERR_CANCELLED) {
538*0867995cSHans Petter Selasky 			/* try clear stall first */
539*0867995cSHans Petter Selasky 			usbd_xfer_set_stall(xfer);
540*0867995cSHans Petter Selasky 			goto tr_setup;
541*0867995cSHans Petter Selasky 		}
542*0867995cSHans Petter Selasky 		break;
543*0867995cSHans Petter Selasky 	}
544*0867995cSHans Petter Selasky 	/* wakeup any waiters */
545*0867995cSHans Petter Selasky 	cv_signal(&sc->sc_cv);
546*0867995cSHans Petter Selasky }
547*0867995cSHans Petter Selasky 
548*0867995cSHans Petter Selasky #if 0
549*0867995cSHans Petter Selasky static int
550*0867995cSHans Petter Selasky udl_power_save(struct udl_softc *sc, int on, int flags)
551*0867995cSHans Petter Selasky {
552*0867995cSHans Petter Selasky 	struct udl_cmd_buf *cb;
553*0867995cSHans Petter Selasky 
554*0867995cSHans Petter Selasky 	/* get new buffer */
555*0867995cSHans Petter Selasky 	cb = udl_cmd_buf_alloc(sc, flags);
556*0867995cSHans Petter Selasky 	if (cb == NULL)
557*0867995cSHans Petter Selasky 		return (EAGAIN);
558*0867995cSHans Petter Selasky 
559*0867995cSHans Petter Selasky 	DPRINTF("screen %s\n", on ? "ON" : "OFF");
560*0867995cSHans Petter Selasky 
561*0867995cSHans Petter Selasky 	sc->sc_power_save = on ? 0 : 1;
562*0867995cSHans Petter Selasky 
563*0867995cSHans Petter Selasky 	if (on)
564*0867995cSHans Petter Selasky 		udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON);
565*0867995cSHans Petter Selasky 	else
566*0867995cSHans Petter Selasky 		udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF);
567*0867995cSHans Petter Selasky 
568*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
569*0867995cSHans Petter Selasky 	udl_cmd_buf_send(sc, cb);
570*0867995cSHans Petter Selasky 	return (0);
571*0867995cSHans Petter Selasky }
572*0867995cSHans Petter Selasky #endif
573*0867995cSHans Petter Selasky 
574*0867995cSHans Petter Selasky static int
575*0867995cSHans Petter Selasky udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r,
576*0867995cSHans Petter Selasky     uint16_t index, uint16_t value, uint8_t *buf, size_t len)
577*0867995cSHans Petter Selasky {
578*0867995cSHans Petter Selasky 	usb_device_request_t req;
579*0867995cSHans Petter Selasky 	int error;
580*0867995cSHans Petter Selasky 
581*0867995cSHans Petter Selasky 	req.bmRequestType = rt;
582*0867995cSHans Petter Selasky 	req.bRequest = r;
583*0867995cSHans Petter Selasky 	USETW(req.wIndex, index);
584*0867995cSHans Petter Selasky 	USETW(req.wValue, value);
585*0867995cSHans Petter Selasky 	USETW(req.wLength, len);
586*0867995cSHans Petter Selasky 
587*0867995cSHans Petter Selasky 	error = usbd_do_request_flags(sc->sc_udev, NULL,
588*0867995cSHans Petter Selasky 	    &req, buf, 0, NULL, USB_DEFAULT_TIMEOUT);
589*0867995cSHans Petter Selasky 
590*0867995cSHans Petter Selasky 	DPRINTF("%s\n", usbd_errstr(error));
591*0867995cSHans Petter Selasky 
592*0867995cSHans Petter Selasky 	return (error);
593*0867995cSHans Petter Selasky }
594*0867995cSHans Petter Selasky 
595*0867995cSHans Petter Selasky static int
596*0867995cSHans Petter Selasky udl_poll(struct udl_softc *sc, uint32_t *buf)
597*0867995cSHans Petter Selasky {
598*0867995cSHans Petter Selasky 	uint32_t lbuf;
599*0867995cSHans Petter Selasky 	int error;
600*0867995cSHans Petter Selasky 
601*0867995cSHans Petter Selasky 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
602*0867995cSHans Petter Selasky 	    UDL_CTRL_CMD_POLL, 0x0000, 0x0000, (uint8_t *)&lbuf, sizeof(lbuf));
603*0867995cSHans Petter Selasky 	if (error == USB_ERR_NORMAL_COMPLETION)
604*0867995cSHans Petter Selasky 		*buf = le32toh(lbuf);
605*0867995cSHans Petter Selasky 	return (error);
606*0867995cSHans Petter Selasky }
607*0867995cSHans Petter Selasky 
608*0867995cSHans Petter Selasky static int
609*0867995cSHans Petter Selasky udl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf)
610*0867995cSHans Petter Selasky {
611*0867995cSHans Petter Selasky 	uint8_t lbuf[1];
612*0867995cSHans Petter Selasky 	int error;
613*0867995cSHans Petter Selasky 
614*0867995cSHans Petter Selasky 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
615*0867995cSHans Petter Selasky 	    UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1);
616*0867995cSHans Petter Selasky 	if (error == USB_ERR_NORMAL_COMPLETION)
617*0867995cSHans Petter Selasky 		*buf = *(uint8_t *)lbuf;
618*0867995cSHans Petter Selasky 	return (error);
619*0867995cSHans Petter Selasky }
620*0867995cSHans Petter Selasky 
621*0867995cSHans Petter Selasky static int
622*0867995cSHans Petter Selasky udl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf)
623*0867995cSHans Petter Selasky {
624*0867995cSHans Petter Selasky 	int error;
625*0867995cSHans Petter Selasky 
626*0867995cSHans Petter Selasky 	error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE,
627*0867995cSHans Petter Selasky 	    UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1);
628*0867995cSHans Petter Selasky 	return (error);
629*0867995cSHans Petter Selasky }
630*0867995cSHans Petter Selasky 
631*0867995cSHans Petter Selasky static int
632*0867995cSHans Petter Selasky udl_read_edid(struct udl_softc *sc, uint8_t *buf)
633*0867995cSHans Petter Selasky {
634*0867995cSHans Petter Selasky 	uint8_t lbuf[64];
635*0867995cSHans Petter Selasky 	uint16_t offset;
636*0867995cSHans Petter Selasky 	int error;
637*0867995cSHans Petter Selasky 
638*0867995cSHans Petter Selasky 	offset = 0;
639*0867995cSHans Petter Selasky 
640*0867995cSHans Petter Selasky 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
641*0867995cSHans Petter Selasky 	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64);
642*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
643*0867995cSHans Petter Selasky 		goto fail;
644*0867995cSHans Petter Selasky 	bcopy(lbuf + 1, buf + offset, 63);
645*0867995cSHans Petter Selasky 	offset += 63;
646*0867995cSHans Petter Selasky 
647*0867995cSHans Petter Selasky 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
648*0867995cSHans Petter Selasky 	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64);
649*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
650*0867995cSHans Petter Selasky 		goto fail;
651*0867995cSHans Petter Selasky 	bcopy(lbuf + 1, buf + offset, 63);
652*0867995cSHans Petter Selasky 	offset += 63;
653*0867995cSHans Petter Selasky 
654*0867995cSHans Petter Selasky 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
655*0867995cSHans Petter Selasky 	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3);
656*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
657*0867995cSHans Petter Selasky 		goto fail;
658*0867995cSHans Petter Selasky 	bcopy(lbuf + 1, buf + offset, 2);
659*0867995cSHans Petter Selasky fail:
660*0867995cSHans Petter Selasky 	return (error);
661*0867995cSHans Petter Selasky }
662*0867995cSHans Petter Selasky 
663*0867995cSHans Petter Selasky static uint8_t
664*0867995cSHans Petter Selasky udl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t hz,
665*0867995cSHans Petter Selasky     uint16_t chip, uint32_t clock)
666*0867995cSHans Petter Selasky {
667*0867995cSHans Petter Selasky 	uint8_t idx;
668*0867995cSHans Petter Selasky 
669*0867995cSHans Petter Selasky 	/*
670*0867995cSHans Petter Selasky 	 * Check first if we have a matching mode with pixelclock
671*0867995cSHans Petter Selasky 	 */
672*0867995cSHans Petter Selasky 	for (idx = 0; idx != UDL_MAX_MODES; idx++) {
673*0867995cSHans Petter Selasky 		if ((udl_modes[idx].hdisplay == hdisplay) &&
674*0867995cSHans Petter Selasky 		    (udl_modes[idx].vdisplay == vdisplay) &&
675*0867995cSHans Petter Selasky 		    (udl_modes[idx].clock == clock) &&
676*0867995cSHans Petter Selasky 		    (udl_modes[idx].chip <= chip)) {
677*0867995cSHans Petter Selasky 			return (idx);
678*0867995cSHans Petter Selasky 		}
679*0867995cSHans Petter Selasky 	}
680*0867995cSHans Petter Selasky 
681*0867995cSHans Petter Selasky 	/*
682*0867995cSHans Petter Selasky 	 * If not, check for matching mode with update frequency
683*0867995cSHans Petter Selasky 	 */
684*0867995cSHans Petter Selasky 	for (idx = 0; idx != UDL_MAX_MODES; idx++) {
685*0867995cSHans Petter Selasky 		if ((udl_modes[idx].hdisplay == hdisplay) &&
686*0867995cSHans Petter Selasky 		    (udl_modes[idx].vdisplay == vdisplay) &&
687*0867995cSHans Petter Selasky 		    (udl_modes[idx].hz == hz) &&
688*0867995cSHans Petter Selasky 		    (udl_modes[idx].chip <= chip)) {
689*0867995cSHans Petter Selasky 			return (idx);
690*0867995cSHans Petter Selasky 		}
691*0867995cSHans Petter Selasky 	}
692*0867995cSHans Petter Selasky 	return (idx);
693*0867995cSHans Petter Selasky }
694*0867995cSHans Petter Selasky 
695*0867995cSHans Petter Selasky static void
696*0867995cSHans Petter Selasky udl_select_chip(struct udl_softc *sc, struct usb_attach_arg *uaa)
697*0867995cSHans Petter Selasky {
698*0867995cSHans Petter Selasky 	const char *pserial;
699*0867995cSHans Petter Selasky 
700*0867995cSHans Petter Selasky 	pserial = usb_get_serial(uaa->device);
701*0867995cSHans Petter Selasky 
702*0867995cSHans Petter Selasky 	sc->sc_chip = DL120;
703*0867995cSHans Petter Selasky 
704*0867995cSHans Petter Selasky 	if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) &&
705*0867995cSHans Petter Selasky 	    (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_WSDVI)) {
706*0867995cSHans Petter Selasky 
707*0867995cSHans Petter Selasky 		/*
708*0867995cSHans Petter Selasky 		 * WS Tech DVI is DL120 or DL160. All deviced uses the
709*0867995cSHans Petter Selasky 		 * same revision (0.04) so iSerialNumber must be used
710*0867995cSHans Petter Selasky 		 * to determin which chip it is.
711*0867995cSHans Petter Selasky 		 */
712*0867995cSHans Petter Selasky 
713*0867995cSHans Petter Selasky 		if (strlen(pserial) > 7) {
714*0867995cSHans Petter Selasky 			if (strncmp(pserial, "0198-13", 7) == 0)
715*0867995cSHans Petter Selasky 				sc->sc_chip = DL160;
716*0867995cSHans Petter Selasky 		}
717*0867995cSHans Petter Selasky 		DPRINTF("iSerialNumber (%s) used to select chip (%d)\n",
718*0867995cSHans Petter Selasky 		    pserial, sc->sc_chip);
719*0867995cSHans Petter Selasky 	}
720*0867995cSHans Petter Selasky 	if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) &&
721*0867995cSHans Petter Selasky 	    (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_SWDVI)) {
722*0867995cSHans Petter Selasky 
723*0867995cSHans Petter Selasky 		/*
724*0867995cSHans Petter Selasky 		 * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision
725*0867995cSHans Petter Selasky 		 * can be used to differ between DL1x0 and DL1x5. Minor to
726*0867995cSHans Petter Selasky 		 * differ between DL1x5. iSerialNumber seems not to be uniqe.
727*0867995cSHans Petter Selasky 		 */
728*0867995cSHans Petter Selasky 
729*0867995cSHans Petter Selasky 		sc->sc_chip = DL160;
730*0867995cSHans Petter Selasky 
731*0867995cSHans Petter Selasky 		if (uaa->info.bcdDevice >= 0x100) {
732*0867995cSHans Petter Selasky 			sc->sc_chip = DL165;
733*0867995cSHans Petter Selasky 			if (uaa->info.bcdDevice == 0x104)
734*0867995cSHans Petter Selasky 				sc->sc_chip = DL195;
735*0867995cSHans Petter Selasky 			if (uaa->info.bcdDevice == 0x108)
736*0867995cSHans Petter Selasky 				sc->sc_chip = DL125;
737*0867995cSHans Petter Selasky 		}
738*0867995cSHans Petter Selasky 		DPRINTF("bcdDevice (%02x) used to select chip (%d)\n",
739*0867995cSHans Petter Selasky 		    uaa->info.bcdDevice, sc->sc_chip);
740*0867995cSHans Petter Selasky 	}
741*0867995cSHans Petter Selasky }
742*0867995cSHans Petter Selasky 
743*0867995cSHans Petter Selasky static int
744*0867995cSHans Petter Selasky udl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len)
745*0867995cSHans Petter Selasky {
746*0867995cSHans Petter Selasky 	int error;
747*0867995cSHans Petter Selasky 
748*0867995cSHans Petter Selasky 	error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE,
749*0867995cSHans Petter Selasky 	    UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len);
750*0867995cSHans Petter Selasky 	return (error);
751*0867995cSHans Petter Selasky }
752*0867995cSHans Petter Selasky 
753*0867995cSHans Petter Selasky static void
754*0867995cSHans Petter Selasky udl_fbmem_alloc(struct udl_softc *sc)
755*0867995cSHans Petter Selasky {
756*0867995cSHans Petter Selasky 	uint32_t size;
757*0867995cSHans Petter Selasky 
758*0867995cSHans Petter Selasky 	size = udl_get_fb_size(sc);
759*0867995cSHans Petter Selasky 	size = round_page(size);
760*0867995cSHans Petter Selasky 
761*0867995cSHans Petter Selasky 	sc->sc_fb_addr = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
762*0867995cSHans Petter Selasky 	sc->sc_fb_copy = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
763*0867995cSHans Petter Selasky 	sc->sc_fb_size = size;
764*0867995cSHans Petter Selasky }
765*0867995cSHans Petter Selasky 
766*0867995cSHans Petter Selasky static void
767*0867995cSHans Petter Selasky udl_cmd_insert_int_1(struct udl_cmd_buf *cb, uint8_t value)
768*0867995cSHans Petter Selasky {
769*0867995cSHans Petter Selasky 
770*0867995cSHans Petter Selasky 	cb->buf[cb->off] = value;
771*0867995cSHans Petter Selasky 	cb->off += 1;
772*0867995cSHans Petter Selasky }
773*0867995cSHans Petter Selasky 
774*0867995cSHans Petter Selasky #if 0
775*0867995cSHans Petter Selasky static void
776*0867995cSHans Petter Selasky udl_cmd_insert_int_2(struct udl_cmd_buf *cb, uint16_t value)
777*0867995cSHans Petter Selasky {
778*0867995cSHans Petter Selasky 	uint16_t lvalue;
779*0867995cSHans Petter Selasky 
780*0867995cSHans Petter Selasky 	lvalue = htobe16(value);
781*0867995cSHans Petter Selasky 	bcopy(&lvalue, cb->buf + cb->off, 2);
782*0867995cSHans Petter Selasky 
783*0867995cSHans Petter Selasky 	cb->off += 2;
784*0867995cSHans Petter Selasky }
785*0867995cSHans Petter Selasky 
786*0867995cSHans Petter Selasky #endif
787*0867995cSHans Petter Selasky 
788*0867995cSHans Petter Selasky static void
789*0867995cSHans Petter Selasky udl_cmd_insert_int_3(struct udl_cmd_buf *cb, uint32_t value)
790*0867995cSHans Petter Selasky {
791*0867995cSHans Petter Selasky 	uint32_t lvalue;
792*0867995cSHans Petter Selasky 
793*0867995cSHans Petter Selasky #if BYTE_ORDER == BIG_ENDIAN
794*0867995cSHans Petter Selasky 	lvalue = htobe32(value) << 8;
795*0867995cSHans Petter Selasky #else
796*0867995cSHans Petter Selasky 	lvalue = htobe32(value) >> 8;
797*0867995cSHans Petter Selasky #endif
798*0867995cSHans Petter Selasky 	bcopy(&lvalue, cb->buf + cb->off, 3);
799*0867995cSHans Petter Selasky 
800*0867995cSHans Petter Selasky 	cb->off += 3;
801*0867995cSHans Petter Selasky }
802*0867995cSHans Petter Selasky 
803*0867995cSHans Petter Selasky #if 0
804*0867995cSHans Petter Selasky static void
805*0867995cSHans Petter Selasky udl_cmd_insert_int_4(struct udl_cmd_buf *cb, uint32_t value)
806*0867995cSHans Petter Selasky {
807*0867995cSHans Petter Selasky 	uint32_t lvalue;
808*0867995cSHans Petter Selasky 
809*0867995cSHans Petter Selasky 	lvalue = htobe32(value);
810*0867995cSHans Petter Selasky 	bcopy(&lvalue, cb->buf + cb->off, 4);
811*0867995cSHans Petter Selasky 
812*0867995cSHans Petter Selasky 	cb->off += 4;
813*0867995cSHans Petter Selasky }
814*0867995cSHans Petter Selasky 
815*0867995cSHans Petter Selasky #endif
816*0867995cSHans Petter Selasky 
817*0867995cSHans Petter Selasky static void
818*0867995cSHans Petter Selasky udl_cmd_insert_buf_le16(struct udl_cmd_buf *cb, const uint8_t *buf, uint32_t len)
819*0867995cSHans Petter Selasky {
820*0867995cSHans Petter Selasky 	uint32_t x;
821*0867995cSHans Petter Selasky 
822*0867995cSHans Petter Selasky 	for (x = 0; x != len; x += 2) {
823*0867995cSHans Petter Selasky 		/* byte swap from little endian to big endian */
824*0867995cSHans Petter Selasky 		cb->buf[cb->off + x + 0] = buf[x + 1];
825*0867995cSHans Petter Selasky 		cb->buf[cb->off + x + 1] = buf[x + 0];
826*0867995cSHans Petter Selasky 	}
827*0867995cSHans Petter Selasky 	cb->off += len;
828*0867995cSHans Petter Selasky }
829*0867995cSHans Petter Selasky 
830*0867995cSHans Petter Selasky static void
831*0867995cSHans Petter Selasky udl_cmd_write_reg_1(struct udl_cmd_buf *cb, uint8_t reg, uint8_t val)
832*0867995cSHans Petter Selasky {
833*0867995cSHans Petter Selasky 
834*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
835*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, UDL_BULK_CMD_REG_WRITE_1);
836*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, reg);
837*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, val);
838*0867995cSHans Petter Selasky }
839*0867995cSHans Petter Selasky 
840*0867995cSHans Petter Selasky static void
841*0867995cSHans Petter Selasky udl_cmd_write_reg_3(struct udl_cmd_buf *cb, uint8_t reg, uint32_t val)
842*0867995cSHans Petter Selasky {
843*0867995cSHans Petter Selasky 
844*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, reg + 0, (val >> 16) & 0xff);
845*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, reg + 1, (val >> 8) & 0xff);
846*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, reg + 2, (val >> 0) & 0xff);
847*0867995cSHans Petter Selasky }
848*0867995cSHans Petter Selasky 
849*0867995cSHans Petter Selasky static int
850*0867995cSHans Petter Selasky udl_init_chip(struct udl_softc *sc)
851*0867995cSHans Petter Selasky {
852*0867995cSHans Petter Selasky 	uint32_t ui32;
853*0867995cSHans Petter Selasky 	uint8_t ui8;
854*0867995cSHans Petter Selasky 	int error;
855*0867995cSHans Petter Selasky 
856*0867995cSHans Petter Selasky 	error = udl_poll(sc, &ui32);
857*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
858*0867995cSHans Petter Selasky 		return (error);
859*0867995cSHans Petter Selasky 	DPRINTF("poll=0x%08x\n", ui32);
860*0867995cSHans Petter Selasky 
861*0867995cSHans Petter Selasky 	/* Some products may use later chip too */
862*0867995cSHans Petter Selasky 	switch (ui32 & 0xff) {
863*0867995cSHans Petter Selasky 	case 0xf1:			/* DL1x5 */
864*0867995cSHans Petter Selasky 		switch (sc->sc_chip) {
865*0867995cSHans Petter Selasky 		case DL120:
866*0867995cSHans Petter Selasky 			sc->sc_chip = DL125;
867*0867995cSHans Petter Selasky 			break;
868*0867995cSHans Petter Selasky 		case DL160:
869*0867995cSHans Petter Selasky 			sc->sc_chip = DL165;
870*0867995cSHans Petter Selasky 			break;
871*0867995cSHans Petter Selasky 		}
872*0867995cSHans Petter Selasky 		break;
873*0867995cSHans Petter Selasky 	}
874*0867995cSHans Petter Selasky 	DPRINTF("chip 0x%04x\n", sc->sc_chip);
875*0867995cSHans Petter Selasky 
876*0867995cSHans Petter Selasky 	error = udl_read_1(sc, 0xc484, &ui8);
877*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
878*0867995cSHans Petter Selasky 		return (error);
879*0867995cSHans Petter Selasky 	DPRINTF("read 0x%02x from 0xc484\n", ui8);
880*0867995cSHans Petter Selasky 
881*0867995cSHans Petter Selasky 	error = udl_write_1(sc, 0xc41f, 0x01);
882*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
883*0867995cSHans Petter Selasky 		return (error);
884*0867995cSHans Petter Selasky 	DPRINTF("write 0x01 to 0xc41f\n");
885*0867995cSHans Petter Selasky 
886*0867995cSHans Petter Selasky 	error = udl_read_edid(sc, sc->sc_edid);
887*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
888*0867995cSHans Petter Selasky 		return (error);
889*0867995cSHans Petter Selasky 	DPRINTF("read EDID\n");
890*0867995cSHans Petter Selasky 
891*0867995cSHans Petter Selasky 	error = udl_set_enc_key(sc, __DECONST(void *, udl_null_key_1),
892*0867995cSHans Petter Selasky 	    sizeof(udl_null_key_1));
893*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
894*0867995cSHans Petter Selasky 		return (error);
895*0867995cSHans Petter Selasky 	DPRINTF("set encryption key\n");
896*0867995cSHans Petter Selasky 
897*0867995cSHans Petter Selasky 	error = udl_write_1(sc, 0xc40b, 0x00);
898*0867995cSHans Petter Selasky 	if (error != USB_ERR_NORMAL_COMPLETION)
899*0867995cSHans Petter Selasky 		return (error);
900*0867995cSHans Petter Selasky 	DPRINTF("write 0x00 to 0xc40b\n");
901*0867995cSHans Petter Selasky 
902*0867995cSHans Petter Selasky 	return (USB_ERR_NORMAL_COMPLETION);
903*0867995cSHans Petter Selasky }
904*0867995cSHans Petter Selasky 
905*0867995cSHans Petter Selasky static void
906*0867995cSHans Petter Selasky udl_init_fb_offsets(struct udl_cmd_buf *cb, uint32_t start16, uint32_t stride16,
907*0867995cSHans Petter Selasky     uint32_t start8, uint32_t stride8)
908*0867995cSHans Petter Selasky {
909*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00);
910*0867995cSHans Petter Selasky 	udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START16, start16);
911*0867995cSHans Petter Selasky 	udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE16, stride16);
912*0867995cSHans Petter Selasky 	udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START8, start8);
913*0867995cSHans Petter Selasky 	udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE8, stride8);
914*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
915*0867995cSHans Petter Selasky }
916*0867995cSHans Petter Selasky 
917*0867995cSHans Petter Selasky static int
918*0867995cSHans Petter Selasky udl_init_resolution(struct udl_softc *sc)
919*0867995cSHans Petter Selasky {
920*0867995cSHans Petter Selasky 	const uint32_t max = udl_get_fb_size(sc);
921*0867995cSHans Petter Selasky 	const uint8_t *buf = udl_modes[sc->sc_cur_mode].mode;
922*0867995cSHans Petter Selasky 	struct udl_cmd_buf *cb;
923*0867995cSHans Petter Selasky 	uint32_t delta;
924*0867995cSHans Petter Selasky 	uint32_t i;
925*0867995cSHans Petter Selasky 	int error;
926*0867995cSHans Petter Selasky 
927*0867995cSHans Petter Selasky 	/* get new buffer */
928*0867995cSHans Petter Selasky 	cb = udl_cmd_buf_alloc(sc, M_WAITOK);
929*0867995cSHans Petter Selasky 	if (cb == NULL)
930*0867995cSHans Petter Selasky 		return (EAGAIN);
931*0867995cSHans Petter Selasky 
932*0867995cSHans Petter Selasky 	/* write resolution values and set video memory offsets */
933*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00);
934*0867995cSHans Petter Selasky 	for (i = 0; i < UDL_MODE_SIZE; i++)
935*0867995cSHans Petter Selasky 		udl_cmd_write_reg_1(cb, i, buf[i]);
936*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
937*0867995cSHans Petter Selasky 
938*0867995cSHans Petter Selasky 	udl_init_fb_offsets(cb, 0x000000, 0x000a00, 0x555555, 0x000500);
939*0867995cSHans Petter Selasky 	udl_cmd_buf_send(sc, cb);
940*0867995cSHans Petter Selasky 
941*0867995cSHans Petter Selasky 	/* fill screen with black color */
942*0867995cSHans Petter Selasky 	for (i = 0; i < max; i += delta) {
943*0867995cSHans Petter Selasky 		static const uint8_t udl_black[UDL_CMD_MAX_PIXEL_COUNT * 2] __aligned(4);
944*0867995cSHans Petter Selasky 
945*0867995cSHans Petter Selasky 		delta = max - i;
946*0867995cSHans Petter Selasky 		if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2)
947*0867995cSHans Petter Selasky 			delta = UDL_CMD_MAX_PIXEL_COUNT * 2;
948*0867995cSHans Petter Selasky 		if (i == 0)
949*0867995cSHans Petter Selasky 			error = udl_cmd_write_buf_le16(sc, udl_black, i, delta / 2, M_WAITOK);
950*0867995cSHans Petter Selasky 		else
951*0867995cSHans Petter Selasky 			error = udl_cmd_buf_copy_le16(sc, 0, i, delta / 2, M_WAITOK);
952*0867995cSHans Petter Selasky 		if (error)
953*0867995cSHans Petter Selasky 			return (error);
954*0867995cSHans Petter Selasky 	}
955*0867995cSHans Petter Selasky 
956*0867995cSHans Petter Selasky 	/* get new buffer */
957*0867995cSHans Petter Selasky 	cb = udl_cmd_buf_alloc(sc, M_WAITOK);
958*0867995cSHans Petter Selasky 	if (cb == NULL)
959*0867995cSHans Petter Selasky 		return (EAGAIN);
960*0867995cSHans Petter Selasky 
961*0867995cSHans Petter Selasky 	/* show framebuffer content */
962*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON);
963*0867995cSHans Petter Selasky 	udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
964*0867995cSHans Petter Selasky 	udl_cmd_buf_send(sc, cb);
965*0867995cSHans Petter Selasky 	return (0);
966*0867995cSHans Petter Selasky }
967*0867995cSHans Petter Selasky 
968*0867995cSHans Petter Selasky static void
969*0867995cSHans Petter Selasky udl_select_mode(struct udl_softc *sc)
970*0867995cSHans Petter Selasky {
971*0867995cSHans Petter Selasky 	struct udl_mode mode;
972*0867995cSHans Petter Selasky 	int index = UDL_MAX_MODES;
973*0867995cSHans Petter Selasky 	int i;
974*0867995cSHans Petter Selasky 
975*0867995cSHans Petter Selasky 	/* try to get the preferred mode from EDID */
976*0867995cSHans Petter Selasky 	edid_parse(sc->sc_edid, &sc->sc_edid_info);
977*0867995cSHans Petter Selasky #ifdef USB_DEBUG
978*0867995cSHans Petter Selasky 	edid_print(&sc->sc_edid_info);
979*0867995cSHans Petter Selasky #endif
980*0867995cSHans Petter Selasky 	if (sc->sc_edid_info.edid_preferred_mode != NULL) {
981*0867995cSHans Petter Selasky 		mode.hz =
982*0867995cSHans Petter Selasky 		    (sc->sc_edid_info.edid_preferred_mode->dot_clock * 1000) /
983*0867995cSHans Petter Selasky 		    (sc->sc_edid_info.edid_preferred_mode->htotal *
984*0867995cSHans Petter Selasky 		    sc->sc_edid_info.edid_preferred_mode->vtotal);
985*0867995cSHans Petter Selasky 		mode.clock =
986*0867995cSHans Petter Selasky 		    sc->sc_edid_info.edid_preferred_mode->dot_clock / 10;
987*0867995cSHans Petter Selasky 		mode.hdisplay =
988*0867995cSHans Petter Selasky 		    sc->sc_edid_info.edid_preferred_mode->hdisplay;
989*0867995cSHans Petter Selasky 		mode.vdisplay =
990*0867995cSHans Petter Selasky 		    sc->sc_edid_info.edid_preferred_mode->vdisplay;
991*0867995cSHans Petter Selasky 		index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, mode.hz,
992*0867995cSHans Petter Selasky 		    sc->sc_chip, mode.clock);
993*0867995cSHans Petter Selasky 		sc->sc_cur_mode = index;
994*0867995cSHans Petter Selasky 	} else {
995*0867995cSHans Petter Selasky 		DPRINTF("no preferred mode found!\n");
996*0867995cSHans Petter Selasky 	}
997*0867995cSHans Petter Selasky 
998*0867995cSHans Petter Selasky 	if (index == UDL_MAX_MODES) {
999*0867995cSHans Petter Selasky 		DPRINTF("no mode line found for %dx%d @ %dHz!\n",
1000*0867995cSHans Petter Selasky 		    mode.hdisplay, mode.vdisplay, mode.hz);
1001*0867995cSHans Petter Selasky 
1002*0867995cSHans Petter Selasky 		i = 0;
1003*0867995cSHans Petter Selasky 		while (i < sc->sc_edid_info.edid_nmodes) {
1004*0867995cSHans Petter Selasky 			mode.hz =
1005*0867995cSHans Petter Selasky 			    (sc->sc_edid_info.edid_modes[i].dot_clock * 1000) /
1006*0867995cSHans Petter Selasky 			    (sc->sc_edid_info.edid_modes[i].htotal *
1007*0867995cSHans Petter Selasky 			    sc->sc_edid_info.edid_modes[i].vtotal);
1008*0867995cSHans Petter Selasky 			mode.clock =
1009*0867995cSHans Petter Selasky 			    sc->sc_edid_info.edid_modes[i].dot_clock / 10;
1010*0867995cSHans Petter Selasky 			mode.hdisplay =
1011*0867995cSHans Petter Selasky 			    sc->sc_edid_info.edid_modes[i].hdisplay;
1012*0867995cSHans Petter Selasky 			mode.vdisplay =
1013*0867995cSHans Petter Selasky 			    sc->sc_edid_info.edid_modes[i].vdisplay;
1014*0867995cSHans Petter Selasky 			index = udl_lookup_mode(mode.hdisplay, mode.vdisplay,
1015*0867995cSHans Petter Selasky 			    mode.hz, sc->sc_chip, mode.clock);
1016*0867995cSHans Petter Selasky 			if (index < UDL_MAX_MODES)
1017*0867995cSHans Petter Selasky 				if ((sc->sc_cur_mode == UDL_MAX_MODES) ||
1018*0867995cSHans Petter Selasky 				    (index > sc->sc_cur_mode))
1019*0867995cSHans Petter Selasky 					sc->sc_cur_mode = index;
1020*0867995cSHans Petter Selasky 			i++;
1021*0867995cSHans Petter Selasky 		}
1022*0867995cSHans Petter Selasky 	}
1023*0867995cSHans Petter Selasky 	/*
1024*0867995cSHans Petter Selasky 	 * If no mode found use default.
1025*0867995cSHans Petter Selasky 	 */
1026*0867995cSHans Petter Selasky 	if (sc->sc_cur_mode == UDL_MAX_MODES)
1027*0867995cSHans Petter Selasky 		sc->sc_cur_mode = udl_lookup_mode(800, 600, 60, sc->sc_chip, 0);
1028*0867995cSHans Petter Selasky }
1029*0867995cSHans Petter Selasky 
1030*0867995cSHans Petter Selasky static int
1031*0867995cSHans Petter Selasky udl_cmd_write_buf_le16(struct udl_softc *sc, const uint8_t *buf, uint32_t off,
1032*0867995cSHans Petter Selasky     uint8_t pixels, int flags)
1033*0867995cSHans Petter Selasky {
1034*0867995cSHans Petter Selasky 	struct udl_cmd_buf *cb;
1035*0867995cSHans Petter Selasky 
1036*0867995cSHans Petter Selasky 	cb = udl_cmd_buf_alloc(sc, flags);
1037*0867995cSHans Petter Selasky 	if (cb == NULL)
1038*0867995cSHans Petter Selasky 		return (EAGAIN);
1039*0867995cSHans Petter Selasky 
1040*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
1041*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD);
1042*0867995cSHans Petter Selasky 	udl_cmd_insert_int_3(cb, off);
1043*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, pixels);
1044*0867995cSHans Petter Selasky 	udl_cmd_insert_buf_le16(cb, buf, 2 * pixels);
1045*0867995cSHans Petter Selasky 	udl_cmd_buf_send(sc, cb);
1046*0867995cSHans Petter Selasky 
1047*0867995cSHans Petter Selasky 	return (0);
1048*0867995cSHans Petter Selasky }
1049*0867995cSHans Petter Selasky 
1050*0867995cSHans Petter Selasky static int
1051*0867995cSHans Petter Selasky udl_cmd_buf_copy_le16(struct udl_softc *sc, uint32_t src, uint32_t dst,
1052*0867995cSHans Petter Selasky     uint8_t pixels, int flags)
1053*0867995cSHans Petter Selasky {
1054*0867995cSHans Petter Selasky 	struct udl_cmd_buf *cb;
1055*0867995cSHans Petter Selasky 
1056*0867995cSHans Petter Selasky 	cb = udl_cmd_buf_alloc(sc, flags);
1057*0867995cSHans Petter Selasky 	if (cb == NULL)
1058*0867995cSHans Petter Selasky 		return (EAGAIN);
1059*0867995cSHans Petter Selasky 
1060*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
1061*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD);
1062*0867995cSHans Petter Selasky 	udl_cmd_insert_int_3(cb, 2 * dst);
1063*0867995cSHans Petter Selasky 	udl_cmd_insert_int_1(cb, pixels);
1064*0867995cSHans Petter Selasky 	udl_cmd_insert_int_3(cb, 2 * src);
1065*0867995cSHans Petter Selasky 	udl_cmd_buf_send(sc, cb);
1066*0867995cSHans Petter Selasky 
1067*0867995cSHans Petter Selasky 	return (0);
1068*0867995cSHans Petter Selasky }
1069