xref: /openbsd-src/sys/dev/usb/udl.c (revision c3de624075efb005639caa4b430c1801747247b5)
1*c3de6240Smiod /*	$OpenBSD: udl.c,v 1.103 2024/11/09 08:26:29 miod Exp $ */
2c197399bSmglocker 
3c197399bSmglocker /*
4c197399bSmglocker  * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org>
5c197399bSmglocker  *
6c197399bSmglocker  * Permission to use, copy, modify, and distribute this software for any
7c197399bSmglocker  * purpose with or without fee is hereby granted, provided that the above
8c197399bSmglocker  * copyright notice and this permission notice appear in all copies.
9c197399bSmglocker  *
10c197399bSmglocker  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c197399bSmglocker  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c197399bSmglocker  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c197399bSmglocker  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c197399bSmglocker  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c197399bSmglocker  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c197399bSmglocker  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c197399bSmglocker  */
18c197399bSmglocker 
19c197399bSmglocker /*
20c197399bSmglocker  * Driver for the ``DisplayLink DL-120 / DL-160'' graphic chips based
21c197399bSmglocker  * on the reversed engineered specifications of Florian Echtler
22c197399bSmglocker  * <floe@butterbrot.org>:
23c197399bSmglocker  *
24c197399bSmglocker  * 	http://floe.butterbrot.org/displaylink/doku.php
25c197399bSmglocker  *
26c197399bSmglocker  * This driver has been inspired by the cfxga(4) driver because we have
27c197399bSmglocker  * to deal with similar challenges, like no direct access to the video
28c197399bSmglocker  * memory.
29c197399bSmglocker  */
30c197399bSmglocker 
31c197399bSmglocker #include <sys/param.h>
32c197399bSmglocker #include <sys/device.h>
33c197399bSmglocker #include <sys/malloc.h>
349448a446Stedu #include <sys/systm.h>
359448a446Stedu 
369448a446Stedu #include <uvm/uvm_extern.h>
37c197399bSmglocker 
38c197399bSmglocker #include <dev/usb/usb.h>
39c197399bSmglocker #include <dev/usb/usbdi.h>
40c197399bSmglocker #include <dev/usb/usbdi_util.h>
41c197399bSmglocker #include <dev/usb/usbdevs.h>
42c197399bSmglocker 
43c197399bSmglocker #include <dev/wscons/wsconsio.h>
44c197399bSmglocker #include <dev/wscons/wsdisplayvar.h>
45c197399bSmglocker #include <dev/rasops/rasops.h>
46c197399bSmglocker 
476e641510Smaja #include <dev/videomode/videomode.h>
486e641510Smaja #include <dev/videomode/edidvar.h>
496e641510Smaja 
50c197399bSmglocker #include <dev/usb/udl.h>
5119a86cbaSmglocker #include <dev/usb/udlio.h>
52c197399bSmglocker 
53c197399bSmglocker /*
54c197399bSmglocker  * Defines.
55c197399bSmglocker  */
56c197399bSmglocker #if 0
57c197399bSmglocker #define UDL_DEBUG
58c197399bSmglocker #endif
59c197399bSmglocker #ifdef UDL_DEBUG
60c197399bSmglocker int udl_debug = 1;
61c197399bSmglocker #define DPRINTF(l, x...) do { if ((l) <= udl_debug) printf(x); } while (0)
62c197399bSmglocker #else
63c197399bSmglocker #define DPRINTF(l, x...)
64c197399bSmglocker #endif
65c197399bSmglocker 
66c197399bSmglocker #define DN(sc)		((sc)->sc_dev.dv_xname)
67c197399bSmglocker #define FUNC		__func__
68c197399bSmglocker 
69c197399bSmglocker /*
70c197399bSmglocker  * Prototypes.
71c197399bSmglocker  */
72c197399bSmglocker int		udl_match(struct device *, void *, void *);
73c197399bSmglocker void		udl_attach(struct device *, struct device *, void *);
74ef89f9e6Smpi void		udl_attach_hook(struct device *);
75c197399bSmglocker int		udl_detach(struct device *, int);
76e78728c7Spirofti int		udl_activate(struct device *, int);
77c197399bSmglocker 
78c197399bSmglocker int		udl_ioctl(void *, u_long, caddr_t, int, struct proc *);
79c197399bSmglocker paddr_t		udl_mmap(void *, off_t, int);
80c197399bSmglocker int		udl_alloc_screen(void *, const struct wsscreen_descr *,
81e0c3e559Sjsg 		    void **, int *, int *, uint32_t *);
82c197399bSmglocker void		udl_free_screen(void *, void *);
83c197399bSmglocker int		udl_show_screen(void *, void *, int,
84c197399bSmglocker 		    void (*)(void *, int, int), void *);
8583275742Smiod int		udl_load_font(void *, void *, struct wsdisplay_font *);
8683275742Smiod int		udl_list_font(void *, struct wsdisplay_font *);
87c197399bSmglocker void		udl_burner(void *, u_int, u_int);
88c197399bSmglocker 
89072953e3Smiod int		udl_copycols(void *, int, int, int, int);
90072953e3Smiod int		udl_copyrows(void *, int, int, int);
91e0c3e559Sjsg int		udl_erasecols(void *, int, int, int, uint32_t);
92e0c3e559Sjsg int		udl_eraserows(void *, int, int, uint32_t);
93e0c3e559Sjsg int		udl_putchar(void *, int, int, u_int, uint32_t);
94072953e3Smiod int		udl_do_cursor(struct rasops_info *);
9523e0be93Smglocker int		udl_draw_char(struct udl_softc *, uint16_t, uint16_t, u_int,
9623e0be93Smglocker 		    uint32_t, uint32_t);
97fa695a51Smglocker int		udl_damage(struct udl_softc *, uint8_t *,
98fa695a51Smglocker 		    uint32_t, uint32_t, uint32_t, uint32_t);
99fa695a51Smglocker int		udl_draw_image(struct udl_softc *, uint8_t *,
100fa695a51Smglocker 		    uint32_t, uint32_t, uint32_t, uint32_t);
101c197399bSmglocker 
102c197399bSmglocker usbd_status	udl_ctrl_msg(struct udl_softc *, uint8_t, uint8_t,
103c197399bSmglocker 		    uint16_t, uint16_t, uint8_t *, size_t);
104c197399bSmglocker usbd_status	udl_poll(struct udl_softc *, uint32_t *);
105c197399bSmglocker usbd_status	udl_read_1(struct udl_softc *, uint16_t, uint8_t *);
106c197399bSmglocker usbd_status	udl_write_1(struct udl_softc *, uint16_t, uint8_t);
107c197399bSmglocker usbd_status	udl_read_edid(struct udl_softc *, uint8_t *);
1086e641510Smaja uint8_t		udl_lookup_mode(uint16_t, uint16_t, uint8_t, uint16_t,
1096e641510Smaja 		    uint32_t);
1106e641510Smaja int		udl_select_chip(struct udl_softc *);
111c197399bSmglocker usbd_status	udl_set_enc_key(struct udl_softc *, uint8_t *, uint8_t);
112c197399bSmglocker usbd_status	udl_set_decomp_table(struct udl_softc *, uint8_t *, uint16_t);
113c197399bSmglocker 
1142fa8dc63Smglocker int		udl_load_huffman(struct udl_softc *);
1152fa8dc63Smglocker void		udl_free_huffman(struct udl_softc *);
116fa695a51Smglocker int		udl_fbmem_alloc(struct udl_softc *);
117fa695a51Smglocker void		udl_fbmem_free(struct udl_softc *);
118c197399bSmglocker usbd_status	udl_cmd_alloc_xfer(struct udl_softc *);
119c197399bSmglocker void		udl_cmd_free_xfer(struct udl_softc *);
120c197399bSmglocker int		udl_cmd_alloc_buf(struct udl_softc *);
121c197399bSmglocker void		udl_cmd_free_buf(struct udl_softc *);
122c197399bSmglocker void		udl_cmd_insert_int_1(struct udl_softc *, uint8_t);
123c197399bSmglocker void		udl_cmd_insert_int_2(struct udl_softc *, uint16_t);
124c197399bSmglocker void		udl_cmd_insert_int_3(struct udl_softc *, uint32_t);
125c197399bSmglocker void		udl_cmd_insert_int_4(struct udl_softc *, uint32_t);
126c197399bSmglocker void		udl_cmd_insert_buf(struct udl_softc *, uint8_t *, uint32_t);
1272fa8dc63Smglocker int		udl_cmd_insert_buf_comp(struct udl_softc *, uint8_t *,
1282fa8dc63Smglocker 		    uint32_t);
1292fa8dc63Smglocker int		udl_cmd_insert_head_comp(struct udl_softc *, uint32_t);
130fc15257dSmglocker int		udl_cmd_insert_check(struct udl_softc *, int);
131b1107686Smglocker void		udl_cmd_set_xfer_type(struct udl_softc *, int);
1328e027a9aSmglocker void		udl_cmd_save_offset(struct udl_softc *);
1338e027a9aSmglocker void		udl_cmd_restore_offset(struct udl_softc *);
134c197399bSmglocker void		udl_cmd_write_reg_1(struct udl_softc *, uint8_t, uint8_t);
135c197399bSmglocker void		udl_cmd_write_reg_3(struct udl_softc *, uint8_t, uint32_t);
136c197399bSmglocker usbd_status	udl_cmd_send(struct udl_softc *);
137c197399bSmglocker usbd_status	udl_cmd_send_async(struct udl_softc *);
138ab0b1be7Smglocker void		udl_cmd_send_async_cb(struct usbd_xfer *, void *, usbd_status);
139c197399bSmglocker 
140c197399bSmglocker usbd_status	udl_init_chip(struct udl_softc *);
141c197399bSmglocker void		udl_init_fb_offsets(struct udl_softc *, uint32_t, uint32_t,
142c197399bSmglocker 		    uint32_t, uint32_t);
1436e641510Smaja usbd_status	udl_init_resolution(struct udl_softc *);
144082da39dSmglocker usbd_status	udl_clear_screen(struct udl_softc *);
1456e641510Smaja void		udl_select_mode(struct udl_softc *);
146fc15257dSmglocker int		udl_fb_buf_write(struct udl_softc *, uint8_t *, uint32_t,
1474be1f5e5Smglocker 		    uint32_t, uint16_t);
14823e0be93Smglocker int		udl_fb_block_write(struct udl_softc *, uint16_t, uint32_t,
149c197399bSmglocker 		    uint32_t, uint32_t, uint32_t);
15023e0be93Smglocker int		udl_fb_line_write(struct udl_softc *, uint16_t, uint32_t,
15123e0be93Smglocker 		    uint32_t, uint32_t);
15223e0be93Smglocker int		udl_fb_off_write(struct udl_softc *, uint16_t, uint32_t,
15323e0be93Smglocker 		    uint16_t);
154fc15257dSmglocker int		udl_fb_block_copy(struct udl_softc *, uint32_t, uint32_t,
155c197399bSmglocker 		    uint32_t, uint32_t, uint32_t, uint32_t);
15623e0be93Smglocker int		udl_fb_line_copy(struct udl_softc *, uint32_t, uint32_t,
1572fa8dc63Smglocker 		    uint32_t, uint32_t, uint32_t);
15823e0be93Smglocker int		udl_fb_off_copy(struct udl_softc *, uint32_t, uint32_t,
15923e0be93Smglocker 		    uint16_t);
160fc15257dSmglocker int		udl_fb_buf_write_comp(struct udl_softc *, uint8_t *, uint32_t,
1612fa8dc63Smglocker 		    uint32_t, uint16_t);
16223e0be93Smglocker int		udl_fb_block_write_comp(struct udl_softc *, uint16_t, uint32_t,
1632fa8dc63Smglocker 		    uint32_t, uint32_t, uint32_t);
16423e0be93Smglocker int		udl_fb_line_write_comp(struct udl_softc *, uint16_t, uint32_t,
16523e0be93Smglocker 		    uint32_t, uint32_t);
16623e0be93Smglocker int		udl_fb_off_write_comp(struct udl_softc *, uint16_t, uint32_t,
16723e0be93Smglocker 		    uint16_t);
168fc15257dSmglocker int		udl_fb_block_copy_comp(struct udl_softc *, uint32_t, uint32_t,
1692fa8dc63Smglocker 		    uint32_t, uint32_t, uint32_t, uint32_t);
17023e0be93Smglocker int		udl_fb_line_copy_comp(struct udl_softc *, uint32_t, uint32_t,
17123e0be93Smglocker 		    uint32_t, uint32_t, uint32_t);
17223e0be93Smglocker int		udl_fb_off_copy_comp(struct udl_softc *, uint32_t, uint32_t,
17323e0be93Smglocker 		    uint16_t);
174c197399bSmglocker #ifdef UDL_DEBUG
175c197399bSmglocker void		udl_hexdump(void *, int, int);
176c197399bSmglocker usbd_status	udl_init_test(struct udl_softc *);
177c197399bSmglocker #endif
178c197399bSmglocker 
179c197399bSmglocker /*
180c197399bSmglocker  * Driver glue.
181c197399bSmglocker  */
182c197399bSmglocker struct cfdriver udl_cd = {
183c197399bSmglocker 	NULL, "udl", DV_DULL
184c197399bSmglocker };
185c197399bSmglocker 
186c197399bSmglocker const struct cfattach udl_ca = {
187c197399bSmglocker 	sizeof(struct udl_softc),
188c197399bSmglocker 	udl_match,
189c197399bSmglocker 	udl_attach,
190c197399bSmglocker 	udl_detach,
191c197399bSmglocker 	udl_activate
192c197399bSmglocker };
193c197399bSmglocker 
194c197399bSmglocker /*
195c197399bSmglocker  * wsdisplay glue.
196c197399bSmglocker  */
197c197399bSmglocker struct wsscreen_descr udl_stdscreen = {
198c197399bSmglocker 	"std",			/* name */
199c197399bSmglocker 	0, 0,			/* ncols, nrows */
200c197399bSmglocker 	NULL,			/* textops */
201c197399bSmglocker 	0, 0,			/* fontwidth, fontheight */
202c197399bSmglocker 	WSSCREEN_WSCOLORS	/* capabilities */
203c197399bSmglocker };
204c197399bSmglocker 
205c197399bSmglocker const struct wsscreen_descr *udl_scrlist[] = {
206c197399bSmglocker 	&udl_stdscreen
207c197399bSmglocker };
208c197399bSmglocker 
209c197399bSmglocker struct wsscreen_list udl_screenlist = {
210c197399bSmglocker 	sizeof(udl_scrlist) / sizeof(struct wsscreen_descr *), udl_scrlist
211c197399bSmglocker };
212c197399bSmglocker 
213c197399bSmglocker struct wsdisplay_accessops udl_accessops = {
21487eec248Smiod 	.ioctl = udl_ioctl,
21587eec248Smiod 	.mmap = udl_mmap,
21687eec248Smiod 	.alloc_screen = udl_alloc_screen,
21787eec248Smiod 	.free_screen = udl_free_screen,
21887eec248Smiod 	.show_screen = udl_show_screen,
21983275742Smiod 	.load_font = udl_load_font,
22083275742Smiod 	.list_font = udl_list_font,
22187eec248Smiod 	.burn_screen = udl_burner
222c197399bSmglocker };
223c197399bSmglocker 
224c197399bSmglocker /*
225c197399bSmglocker  * Matching devices.
226c197399bSmglocker  */
2276e641510Smaja struct udl_type {
2286e641510Smaja 	struct usb_devno	udl_dev;
22953fd709fSmglocker 	uint16_t		udl_chip;
230c197399bSmglocker };
231c197399bSmglocker 
2326e641510Smaja static const struct udl_type udl_devs[] = {
233a5a22e49Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020 },	DL160 },
2346e641510Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220 },	DL165 },
235*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD190 },	DLUNK },
236*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_U70 },	DLUNK },
237*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_TOSHIBA },  DLUNK },
238*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2 },	DLUNK },
2396e641510Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60 },	DL160 },
240*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV },	DL160 },
2416e641510Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI },	DL160 },
242*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_USBRGB },	DLUNK },
243*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCDUSB7X },	DLUNK },
244*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCDUSB10X },
245*c3de6240Smiod 	    DLUNK },
2466e641510Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10 },	DL120 },
2476e641510Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI },	DLUNK },
2486e641510Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008 },	DL160 },
249*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_FYDVI2 },	DLUNK },
250*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GXDVIU2 },	DLUNK },
251*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U },	DL120 },
252*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U },	DL120 },
2536e641510Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK },	DL160 },
254a5a22e49Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571 },	DL160 },
2556e641510Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061 },	DL195 },
256a5a22e49Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK },	DL165 },
257*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GXDVIU2B },	DLUNK },
258825f6ef7Smaja 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI },	DLUNK },
259712154e1Sclaudio 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70 },	DL125 },
260*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000UD_DVI },
261*c3de6240Smiod 	    DLUNK },
262*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LDEWX015U },
263*c3de6240Smiod 	    DLUNK },
264*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_KC002N },	DLUNK },
265*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_MIMO },	DLUNK },
266*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE },	DLUNK },
2671f0c4d66Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421 },	DLUNK },
268*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SD_U2VDH },	DLUNK },
269*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0 },	DL120 },
270*c3de6240Smiod 	{ { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_FYDVI },	DLUNK }
2716e641510Smaja };
2726e641510Smaja #define udl_lookup(v, p) ((struct udl_type *)usb_lookup(udl_devs, v, p))
2736e641510Smaja 
274c197399bSmglocker int
275c197399bSmglocker udl_match(struct device *parent, void *match, void *aux)
276c197399bSmglocker {
277c197399bSmglocker 	struct usb_attach_arg *uaa = aux;
278c197399bSmglocker 
279f4b7d08eSmpi 	if (uaa->iface == NULL || uaa->configno != 1)
280c197399bSmglocker 		return (UMATCH_NONE);
281c197399bSmglocker 
2826e641510Smaja 	if (udl_lookup(uaa->vendor, uaa->product) != NULL)
283c197399bSmglocker 		return (UMATCH_VENDOR_PRODUCT);
284c197399bSmglocker 
285c197399bSmglocker 	return (UMATCH_NONE);
286c197399bSmglocker }
287c197399bSmglocker 
288c197399bSmglocker void
289c197399bSmglocker udl_attach(struct device *parent, struct device *self, void *aux)
290c197399bSmglocker {
291c197399bSmglocker 	struct udl_softc *sc = (struct udl_softc *)self;
292c197399bSmglocker 	struct usb_attach_arg *uaa = aux;
293c197399bSmglocker 	struct wsemuldisplaydev_attach_args aa;
294c197399bSmglocker 	usbd_status error;
29553fd709fSmglocker 	int err, i;
296c197399bSmglocker 
297c197399bSmglocker 	sc->sc_udev = uaa->device;
2986e641510Smaja 	sc->sc_chip = udl_lookup(uaa->vendor, uaa->product)->udl_chip;
2996e641510Smaja 	sc->sc_width = 0;
3006e641510Smaja 	sc->sc_height = 0;
3016e641510Smaja 	sc->sc_depth = 16;
3026e641510Smaja 	sc->sc_cur_mode = MAX_DL_MODES;
3036e641510Smaja 
3046e641510Smaja 	/*
3056e641510Smaja 	 * Override chip if requested.
3066e641510Smaja 	 */
3076e641510Smaja 	if ((sc->sc_dev.dv_cfdata->cf_flags & 0xff00) > 0) {
3086e641510Smaja 		i = ((sc->sc_dev.dv_cfdata->cf_flags & 0xff00) >> 8) - 1;
3096e641510Smaja 		if (i <= DLMAX) {
3106e641510Smaja 			sc->sc_chip = i;
3116e641510Smaja 			printf("%s: %s: cf_flags (0x%04x) forced chip to %d\n",
3126e641510Smaja 			    DN(sc), FUNC,
3136e641510Smaja 			    sc->sc_dev.dv_cfdata->cf_flags, i);
3146e641510Smaja 		}
3156e641510Smaja 	}
3166e641510Smaja 
3176e641510Smaja 	/*
3186e641510Smaja 	 * The product might have more than one chip
3196e641510Smaja 	 */
3206e641510Smaja 	if (sc->sc_chip == DLUNK)
3216e641510Smaja 		if (udl_select_chip(sc))
3226e641510Smaja 			return;
323c197399bSmglocker 
324c197399bSmglocker 
325c197399bSmglocker 	/*
326c197399bSmglocker 	 * Create device handle to interface descriptor.
327c197399bSmglocker 	 */
328c197399bSmglocker 	error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface);
329c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
330c197399bSmglocker 		return;
331c197399bSmglocker 
332c197399bSmglocker 	/*
333c197399bSmglocker 	 * Allocate bulk command xfer.
334c197399bSmglocker 	 */
335c197399bSmglocker 	error = udl_cmd_alloc_xfer(sc);
336c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
337c197399bSmglocker 		return;
338c197399bSmglocker 
339c197399bSmglocker 	/*
340c197399bSmglocker 	 * Allocate command buffer.
341c197399bSmglocker 	 */
342c197399bSmglocker 	err = udl_cmd_alloc_buf(sc);
343c197399bSmglocker 	if (err != 0)
344c197399bSmglocker 		return;
345c197399bSmglocker 
346c197399bSmglocker 	/*
347c197399bSmglocker 	 * Open bulk TX pipe.
348c197399bSmglocker 	 */
349c197399bSmglocker 	error = usbd_open_pipe(sc->sc_iface, 0x01, USBD_EXCLUSIVE_USE,
350c197399bSmglocker 	    &sc->sc_tx_pipeh);
351c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
352c197399bSmglocker 		return;
353c197399bSmglocker 
354c197399bSmglocker 	/*
355fc15257dSmglocker 	 * Device initialization is done per synchronous xfers.
356fc15257dSmglocker 	 */
357b1107686Smglocker 	udl_cmd_set_xfer_type(sc, UDL_CMD_XFER_SYNC);
358fc15257dSmglocker 
359fc15257dSmglocker 	/*
360c197399bSmglocker 	 * Initialize chip.
361c197399bSmglocker 	 */
362c197399bSmglocker 	error = udl_init_chip(sc);
363c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
364c197399bSmglocker 		return;
365c197399bSmglocker 
366c197399bSmglocker 	/*
3676e641510Smaja 	 * Select edid mode.
368c197399bSmglocker 	 */
3696e641510Smaja 	udl_select_mode(sc);
370c197399bSmglocker 
3716e641510Smaja 	/*
3726e641510Smaja 	 * Override mode if requested.
3736e641510Smaja 	 */
3746e641510Smaja 	if ((sc->sc_dev.dv_cfdata->cf_flags & 0xff) > 0) {
37553fd709fSmglocker 		i = (sc->sc_dev.dv_cfdata->cf_flags & 0xff) - 1;
3766e641510Smaja 
3776e641510Smaja 		if (i < MAX_DL_MODES) {
3786e641510Smaja 			if (udl_modes[i].chip <= sc->sc_chip) {
3796e641510Smaja 				sc->sc_width = udl_modes[i].hdisplay;
3806e641510Smaja 				sc->sc_height = udl_modes[i].vdisplay;
38153fd709fSmglocker 				printf("%s: %s: cf_flags (0x%04x) ",
3826e641510Smaja 				    DN(sc), FUNC,
38353fd709fSmglocker 				    sc->sc_dev.dv_cfdata->cf_flags);
38453fd709fSmglocker 				printf("forced mode to %d\n", i);
3856e641510Smaja 				sc->sc_cur_mode = i;
3866e641510Smaja 			}
3876e641510Smaja 		}
3886e641510Smaja 	}
3896e641510Smaja 
3906e641510Smaja 	error = udl_init_resolution(sc);
391c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
392c197399bSmglocker 		return;
393c197399bSmglocker 
394c197399bSmglocker 	/*
395c197399bSmglocker 	 * Attach wsdisplay.
396c197399bSmglocker 	 */
397c197399bSmglocker 	aa.console = 0;
398c197399bSmglocker 	aa.scrdata = &udl_screenlist;
399c197399bSmglocker 	aa.accessops = &udl_accessops;
400c197399bSmglocker 	aa.accesscookie = sc;
401c197399bSmglocker 	aa.defaultscreens = 0;
402c197399bSmglocker 
403c197399bSmglocker 	sc->sc_wsdisplay = config_found(self, &aa, wsemuldisplaydevprint);
404c197399bSmglocker 
4052fa8dc63Smglocker 	/*
4062fa8dc63Smglocker 	 * Load Huffman table.
4072fa8dc63Smglocker 	 */
408ef89f9e6Smpi 	config_mountroot(self, udl_attach_hook);
4092fa8dc63Smglocker }
4102fa8dc63Smglocker 
4112fa8dc63Smglocker void
412ef89f9e6Smpi udl_attach_hook(struct device *self)
4132fa8dc63Smglocker {
414ef89f9e6Smpi 	struct udl_softc *sc = (struct udl_softc *)self;
4152fa8dc63Smglocker 
4162fa8dc63Smglocker 	if (udl_load_huffman(sc) != 0) {
4172fa8dc63Smglocker 		/* compression not possible */
4182fa8dc63Smglocker 		printf("%s: run in uncompressed mode\n", DN(sc));
4192fa8dc63Smglocker 		sc->udl_fb_buf_write = udl_fb_buf_write;
4209f2543cfSmglocker 		sc->udl_fb_block_write = udl_fb_block_write;
4219f2543cfSmglocker 		sc->udl_fb_line_write = udl_fb_line_write;
4229f2543cfSmglocker 		sc->udl_fb_off_write = udl_fb_off_write;
4232fa8dc63Smglocker 		sc->udl_fb_block_copy = udl_fb_block_copy;
4249f2543cfSmglocker 		sc->udl_fb_line_copy = udl_fb_line_copy;
4259f2543cfSmglocker 		sc->udl_fb_off_copy = udl_fb_off_copy;
4262fa8dc63Smglocker 	} else {
4272fa8dc63Smglocker 		/* compression possible */
4282fa8dc63Smglocker 		sc->udl_fb_buf_write = udl_fb_buf_write_comp;
4299f2543cfSmglocker 		sc->udl_fb_block_write = udl_fb_block_write_comp;
4309f2543cfSmglocker 		sc->udl_fb_line_write = udl_fb_line_write_comp;
4319f2543cfSmglocker 		sc->udl_fb_off_write = udl_fb_off_write_comp;
4322fa8dc63Smglocker 		sc->udl_fb_block_copy = udl_fb_block_copy_comp;
4339f2543cfSmglocker 		sc->udl_fb_line_copy = udl_fb_line_copy_comp;
4349f2543cfSmglocker 		sc->udl_fb_off_copy = udl_fb_off_copy_comp;
4352fa8dc63Smglocker 	}
4362fa8dc63Smglocker #ifdef UDL_DEBUG
437ab5cedacSmglocker 	if (udl_debug >= 4)
4382fa8dc63Smglocker 		udl_init_test(sc);
4392fa8dc63Smglocker #endif
440fc15257dSmglocker 	/*
441fc15257dSmglocker 	 * From this point on we do asynchronous xfers.
442fc15257dSmglocker 	 */
443b1107686Smglocker 	udl_cmd_set_xfer_type(sc, UDL_CMD_XFER_ASYNC);
444082da39dSmglocker 
445082da39dSmglocker 	/*
446082da39dSmglocker 	 * Set initial wsdisplay emulation mode.
447082da39dSmglocker 	 */
448082da39dSmglocker 	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
449c197399bSmglocker }
450c197399bSmglocker 
451c197399bSmglocker int
452c197399bSmglocker udl_detach(struct device *self, int flags)
453c197399bSmglocker {
454c197399bSmglocker 	struct udl_softc *sc = (struct udl_softc *)self;
455c197399bSmglocker 
456c197399bSmglocker 	/*
457c197399bSmglocker 	 * Close bulk TX pipe.
458c197399bSmglocker 	 */
459f88cb03eSmglocker 	if (sc->sc_tx_pipeh != NULL)
460c197399bSmglocker 		usbd_close_pipe(sc->sc_tx_pipeh);
461c197399bSmglocker 
462c197399bSmglocker 	/*
463c197399bSmglocker 	 * Free command buffer.
464c197399bSmglocker 	 */
465c197399bSmglocker 	udl_cmd_free_buf(sc);
466c197399bSmglocker 
467c197399bSmglocker 	/*
468c197399bSmglocker 	 * Free command xfer.
469c197399bSmglocker 	 */
470c197399bSmglocker 	udl_cmd_free_xfer(sc);
471c197399bSmglocker 
472c197399bSmglocker 	/*
4732fa8dc63Smglocker 	 * Free Huffman table.
4742fa8dc63Smglocker 	 */
4752fa8dc63Smglocker 	udl_free_huffman(sc);
4762fa8dc63Smglocker 
4772fa8dc63Smglocker 	/*
478fa695a51Smglocker 	 * Free framebuffer memory.
479fa695a51Smglocker 	 */
480fa695a51Smglocker 	udl_fbmem_free(sc);
481fa695a51Smglocker 
482fa695a51Smglocker 	/*
483c197399bSmglocker 	 * Detach wsdisplay.
484c197399bSmglocker 	 */
485c197399bSmglocker 	if (sc->sc_wsdisplay != NULL)
486c197399bSmglocker 		config_detach(sc->sc_wsdisplay, DETACH_FORCE);
487c197399bSmglocker 
488c197399bSmglocker 	return (0);
489c197399bSmglocker }
490c197399bSmglocker 
491c197399bSmglocker int
492e78728c7Spirofti udl_activate(struct device *self, int act)
493c197399bSmglocker {
494e447d11aSjakemsr 	struct udl_softc *sc = (struct udl_softc *)self;
49537ecb596Sderaadt 	int rv;
496e447d11aSjakemsr 
497c197399bSmglocker 	switch (act) {
498c197399bSmglocker 	case DVACT_DEACTIVATE:
499e447d11aSjakemsr 		usbd_deactivate(sc->sc_udev);
500c197399bSmglocker 		break;
501c197399bSmglocker 	}
50237ecb596Sderaadt 	rv = config_activate_children(self, act);
50337ecb596Sderaadt 	return (rv);
504c197399bSmglocker }
505c197399bSmglocker 
506c197399bSmglocker /* ---------- */
507c197399bSmglocker 
508c197399bSmglocker int
509c197399bSmglocker udl_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
510c197399bSmglocker {
511c197399bSmglocker 	struct udl_softc *sc;
512fa695a51Smglocker 	struct wsdisplay_fbinfo *wdf;
513fa695a51Smglocker 	struct udl_ioctl_damage *d;
514f4611ff0Smglocker 	int r, error, mode;
515c197399bSmglocker 
516c197399bSmglocker 	sc = v;
517c197399bSmglocker 
51836a555e0Smglocker 	DPRINTF(1, "%s: %s: ('%c', %zu, %zu)\n",
51936a555e0Smglocker 	    DN(sc), FUNC, (int) IOCGROUP(cmd), cmd & 0xff, IOCPARM_LEN(cmd));
520c197399bSmglocker 
521c197399bSmglocker 	switch (cmd) {
522c197399bSmglocker 	case WSDISPLAYIO_GTYPE:
523c197399bSmglocker 		*(u_int *)data = WSDISPLAY_TYPE_DL;
524c197399bSmglocker 		break;
525fa695a51Smglocker 	case WSDISPLAYIO_GINFO:
526fa695a51Smglocker 		wdf = (struct wsdisplay_fbinfo *)data;
527fa695a51Smglocker 		wdf->height = sc->sc_height;
528fa695a51Smglocker 		wdf->width = sc->sc_width;
529fa695a51Smglocker 		wdf->depth = sc->sc_depth;
53063294167Skettenis 		wdf->stride = sc->sc_width * (sc->sc_depth / 8);
53163294167Skettenis 		wdf->offset = 0;
532fa695a51Smglocker 		wdf->cmsize = 0;	/* XXX fill up colormap size */
533fa695a51Smglocker 		break;
5345c18bb44Smglocker 	case WSDISPLAYIO_SMODE:
5355c18bb44Smglocker 		mode = *(u_int *)data;
5365c18bb44Smglocker 		if (mode == sc->sc_mode)
5375c18bb44Smglocker 			break;
5385c18bb44Smglocker 		switch (mode) {
5395c18bb44Smglocker 		case WSDISPLAYIO_MODE_EMUL:
540541a4372Smglocker 			/* clear screen */
541082da39dSmglocker 			(void)udl_clear_screen(sc);
5425c18bb44Smglocker 			break;
5435c18bb44Smglocker 		case WSDISPLAYIO_MODE_DUMBFB:
5445c18bb44Smglocker 			/* TODO */
5455c18bb44Smglocker 			break;
546082da39dSmglocker 		default:
547082da39dSmglocker 			return (EINVAL);
5485c18bb44Smglocker 		}
5495c18bb44Smglocker 		sc->sc_mode = mode;
5505c18bb44Smglocker 		break;
551fa695a51Smglocker 	case WSDISPLAYIO_LINEBYTES:
552fa695a51Smglocker 		*(u_int *)data = sc->sc_width * (sc->sc_depth / 8);
553fa695a51Smglocker 		break;
5545c18bb44Smglocker 	case WSDISPLAYIO_SVIDEO:
5555c18bb44Smglocker 	case WSDISPLAYIO_GVIDEO:
5565c18bb44Smglocker 		/* handled for us by wscons */
5575c18bb44Smglocker 		break;
558fa695a51Smglocker 	case UDLIO_DAMAGE:
559fa695a51Smglocker 		d = (struct udl_ioctl_damage *)data;
560f4611ff0Smglocker 		d->status = UDLIO_STATUS_OK;
561fa695a51Smglocker 		r = udl_damage(sc, sc->sc_fbmem, d->x1, d->x2, d->y1, d->y2);
562fa695a51Smglocker 		if (r != 0) {
5631394fe5fSmpi 			error = tsleep_nsec(sc, 0, "udlio", MSEC_TO_NSEC(10));
564f4611ff0Smglocker 			if (error) {
56503b640e7Smglocker 				d->status = UDLIO_STATUS_FAILED;
56603b640e7Smglocker 			} else {
567f4611ff0Smglocker 				r = udl_damage(sc, sc->sc_fbmem, d->x1, d->x2,
568f4611ff0Smglocker 				    d->y1, d->y2);
569f4611ff0Smglocker 				if (r != 0)
570f4611ff0Smglocker 					d->status = UDLIO_STATUS_FAILED;
571f4611ff0Smglocker 			}
572fa695a51Smglocker 		}
573fa695a51Smglocker 		break;
574c197399bSmglocker 	default:
575c197399bSmglocker 		return (-1);
576c197399bSmglocker 	}
577c197399bSmglocker 
578c197399bSmglocker 	return (0);
579c197399bSmglocker }
580c197399bSmglocker 
581c197399bSmglocker paddr_t
582c197399bSmglocker udl_mmap(void *v, off_t off, int prot)
583c197399bSmglocker {
584c197399bSmglocker 	struct udl_softc *sc;
585fa695a51Smglocker 	caddr_t p;
586fa695a51Smglocker 	paddr_t pa;
587c197399bSmglocker 
588c197399bSmglocker 	sc = v;
589c197399bSmglocker 
590c197399bSmglocker 	DPRINTF(1, "%s: %s\n", DN(sc), FUNC);
591c197399bSmglocker 
592fa695a51Smglocker 	/* allocate framebuffer memory */
593fa695a51Smglocker 	if (udl_fbmem_alloc(sc) == -1)
594699280b1Smglocker 		return (-1);
595fa695a51Smglocker 
596fa695a51Smglocker 	/* return memory address to userland process */
597fa695a51Smglocker 	p = sc->sc_fbmem + off;
598fa695a51Smglocker 	if (pmap_extract(pmap_kernel(), (vaddr_t)p, &pa) == FALSE) {
599fa695a51Smglocker 		printf("udl_mmap: invalid page\n");
600fa695a51Smglocker 		udl_fbmem_free(sc);
601fa695a51Smglocker 		return (-1);
602fa695a51Smglocker 	}
603fa695a51Smglocker 	return (pa);
604c197399bSmglocker }
605c197399bSmglocker 
606c197399bSmglocker int
607c197399bSmglocker udl_alloc_screen(void *v, const struct wsscreen_descr *type,
608e0c3e559Sjsg     void **cookiep, int *curxp, int *curyp, uint32_t *attrp)
609c197399bSmglocker {
610c197399bSmglocker 	struct udl_softc *sc = v;
611c197399bSmglocker 	struct wsdisplay_font *font;
612c197399bSmglocker 
613c197399bSmglocker 	DPRINTF(1, "%s: %s\n", DN(sc), FUNC);
614c197399bSmglocker 
615c197399bSmglocker 	if (sc->sc_nscreens > 0)
616c197399bSmglocker 		return (ENOMEM);
617c197399bSmglocker 
618c197399bSmglocker 	/*
619c197399bSmglocker 	 * Initialize rasops.
620c197399bSmglocker 	 */
621c197399bSmglocker 	sc->sc_ri.ri_depth = sc->sc_depth;
622c197399bSmglocker 	sc->sc_ri.ri_bits = NULL;
623c197399bSmglocker 	sc->sc_ri.ri_width = sc->sc_width;
624c197399bSmglocker 	sc->sc_ri.ri_height = sc->sc_height;
625c197399bSmglocker 	sc->sc_ri.ri_stride = sc->sc_width * sc->sc_height / 8;
626c197399bSmglocker 	sc->sc_ri.ri_hw = (void *)sc;
627c197399bSmglocker 	sc->sc_ri.ri_flg = 0;
628c197399bSmglocker 
629c197399bSmglocker 	/* swap B and R at 16 bpp */
630c197399bSmglocker 	if (sc->sc_depth == 16) {
631c197399bSmglocker 		sc->sc_ri.ri_rnum = 5;
632c197399bSmglocker 		sc->sc_ri.ri_rpos = 11;
633c197399bSmglocker 		sc->sc_ri.ri_gnum = 6;
634c197399bSmglocker 		sc->sc_ri.ri_gpos = 5;
635c197399bSmglocker 		sc->sc_ri.ri_bnum = 5;
636c197399bSmglocker 		sc->sc_ri.ri_bpos = 0;
637c197399bSmglocker 	}
638c197399bSmglocker 
6396e641510Smaja 	rasops_init(&sc->sc_ri, 100, 200);
640c197399bSmglocker 
641c197399bSmglocker 	sc->sc_ri.ri_ops.copycols = udl_copycols;
642c197399bSmglocker 	sc->sc_ri.ri_ops.copyrows = udl_copyrows;
643c197399bSmglocker 	sc->sc_ri.ri_ops.erasecols = udl_erasecols;
644c197399bSmglocker 	sc->sc_ri.ri_ops.eraserows = udl_eraserows;
645c197399bSmglocker 	sc->sc_ri.ri_ops.putchar = udl_putchar;
646c197399bSmglocker 	sc->sc_ri.ri_do_cursor = udl_do_cursor;
647c197399bSmglocker 
648fc223b23Sjsg 	sc->sc_ri.ri_ops.pack_attr(&sc->sc_ri, 0, 0, 0, attrp);
649c197399bSmglocker 
650c197399bSmglocker 	udl_stdscreen.nrows = sc->sc_ri.ri_rows;
651c197399bSmglocker 	udl_stdscreen.ncols = sc->sc_ri.ri_cols;
652c197399bSmglocker 	udl_stdscreen.textops = &sc->sc_ri.ri_ops;
653c197399bSmglocker 	udl_stdscreen.fontwidth = sc->sc_ri.ri_font->fontwidth;
654c197399bSmglocker 	udl_stdscreen.fontheight = sc->sc_ri.ri_font->fontheight;
655c197399bSmglocker 	udl_stdscreen.capabilities = sc->sc_ri.ri_caps;
656c197399bSmglocker 
657c197399bSmglocker 	*cookiep = &sc->sc_ri;
658c197399bSmglocker 	*curxp = 0;
659c197399bSmglocker 	*curyp = 0;
660c197399bSmglocker 
661c91d565bSmglocker 	/* allocate character backing store */
6620f0d0f95Sdoug 	sc->sc_cbs = mallocarray(sc->sc_ri.ri_rows, sc->sc_ri.ri_cols *
6638985a220Smglocker 	    sizeof(*sc->sc_cbs), M_USBDEV, M_NOWAIT|M_ZERO);
664c91d565bSmglocker 	if (sc->sc_cbs == NULL) {
665c91d565bSmglocker 		printf("%s: can't allocate mem for character backing store!\n",
666c91d565bSmglocker 		    DN(sc));
667c91d565bSmglocker 		return (ENOMEM);
668c91d565bSmglocker 	}
669234dfda1Sderaadt 	sc->sc_cbslen = sc->sc_ri.ri_rows * sc->sc_ri.ri_cols *
670234dfda1Sderaadt 	    sizeof(*sc->sc_cbs);
671c91d565bSmglocker 
672c197399bSmglocker 	sc->sc_nscreens++;
673c197399bSmglocker 
674c197399bSmglocker 	font = sc->sc_ri.ri_font;
675c197399bSmglocker 	DPRINTF(1, "%s: %s: using font %s (%dx%d)\n",
676c197399bSmglocker 	    DN(sc), FUNC, font->name, sc->sc_ri.ri_cols, sc->sc_ri.ri_rows);
677c197399bSmglocker 
678c197399bSmglocker 	return (0);
679c197399bSmglocker }
680c197399bSmglocker 
681c197399bSmglocker void
682c197399bSmglocker udl_free_screen(void *v, void *cookie)
683c197399bSmglocker {
684c197399bSmglocker 	struct udl_softc *sc;
685c197399bSmglocker 
686c197399bSmglocker 	sc = v;
687c197399bSmglocker 
688c197399bSmglocker 	DPRINTF(1, "%s: %s\n", DN(sc), FUNC);
6896e641510Smaja 
690c91d565bSmglocker 	/* free character backing store */
691c91d565bSmglocker 	if (sc->sc_cbs != NULL)
6928985a220Smglocker 		free(sc->sc_cbs, M_USBDEV, sc->sc_cbslen);
693c91d565bSmglocker 
6946e641510Smaja 	sc->sc_nscreens--;
695c197399bSmglocker }
696c197399bSmglocker 
697c197399bSmglocker int
698c197399bSmglocker udl_show_screen(void *v, void *cookie, int waitok,
699c197399bSmglocker     void (*cb)(void *, int, int), void *cbarg)
700c197399bSmglocker {
701c197399bSmglocker 	struct udl_softc *sc;
702c197399bSmglocker 
703c197399bSmglocker 	sc = v;
704c197399bSmglocker 
705c197399bSmglocker 	DPRINTF(1, "%s: %s\n", DN(sc), FUNC);
706c197399bSmglocker 
707c197399bSmglocker 	return (0);
708c197399bSmglocker }
709c197399bSmglocker 
71083275742Smiod int
71183275742Smiod udl_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
71283275742Smiod {
71383275742Smiod 	struct udl_softc *sc = v;
71483275742Smiod 	struct rasops_info *ri = &sc->sc_ri;
71583275742Smiod 
71683275742Smiod 	return rasops_load_font(ri, emulcookie, font);
71783275742Smiod }
71883275742Smiod 
71983275742Smiod int
72083275742Smiod udl_list_font(void *v, struct wsdisplay_font *font)
72183275742Smiod {
72283275742Smiod 	struct udl_softc *sc = v;
72383275742Smiod 	struct rasops_info *ri = &sc->sc_ri;
72483275742Smiod 
72583275742Smiod 	return rasops_list_font(ri, font);
72683275742Smiod }
72783275742Smiod 
728c197399bSmglocker void
729c197399bSmglocker udl_burner(void *v, u_int on, u_int flags)
730c197399bSmglocker {
731c197399bSmglocker 	struct udl_softc *sc;
732c197399bSmglocker 
733c197399bSmglocker 	sc = v;
734c197399bSmglocker 
73592fdbf9fSmglocker 	DPRINTF(1, "%s: %s: screen %s\n", DN(sc), FUNC, on ? "ON" : "OFF");
736c197399bSmglocker 
737c197399bSmglocker 	if (on)
73892fdbf9fSmglocker 		udl_cmd_write_reg_1(sc, UDL_REG_SCREEN, UDL_REG_SCREEN_ON);
739c197399bSmglocker 	else
74092fdbf9fSmglocker 		udl_cmd_write_reg_1(sc, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF);
741c197399bSmglocker 
742c197399bSmglocker 	udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
743c197399bSmglocker 
74492fdbf9fSmglocker 	(void)udl_cmd_send_async(sc);
745c197399bSmglocker }
746c197399bSmglocker 
747c197399bSmglocker /* ---------- */
748c197399bSmglocker 
749072953e3Smiod int
750c197399bSmglocker udl_copycols(void *cookie, int row, int src, int dst, int num)
751c197399bSmglocker {
752c197399bSmglocker 	struct rasops_info *ri = cookie;
753c197399bSmglocker 	struct udl_softc *sc;
754fc15257dSmglocker 	int sx, sy, dx, dy, cx, cy, r;
7558c7540bbSmglocker 	usbd_status error;
756c197399bSmglocker 
757c197399bSmglocker 	sc = ri->ri_hw;
758c197399bSmglocker 
759c197399bSmglocker 	DPRINTF(2, "%s: %s: row=%d, src=%d, dst=%d, num=%d\n",
760c197399bSmglocker 	    DN(sc), FUNC, row, src, dst, num);
761c197399bSmglocker 
7628e027a9aSmglocker 	udl_cmd_save_offset(sc);
7638c7540bbSmglocker 
764c197399bSmglocker 	sx = src * ri->ri_font->fontwidth;
765c197399bSmglocker 	sy = row * ri->ri_font->fontheight;
766c197399bSmglocker 	dx = dst * ri->ri_font->fontwidth;
767c197399bSmglocker 	dy = row * ri->ri_font->fontheight;
768c197399bSmglocker 	cx = num * ri->ri_font->fontwidth;
769c197399bSmglocker 	cy = ri->ri_font->fontheight;
770c197399bSmglocker 
77137b91793Smglocker 	/* copy row block to off-screen first to fix overlay-copy problem */
772fc15257dSmglocker 	r = (sc->udl_fb_block_copy)
773fc15257dSmglocker 	    (sc, sx, sy, 0, sc->sc_ri.ri_emuheight, cx, cy);
774fc15257dSmglocker 	if (r != 0)
775fc15257dSmglocker 		goto fail;
77637b91793Smglocker 
77737b91793Smglocker 	/* copy row block back from off-screen now */
778fc15257dSmglocker 	r = (sc->udl_fb_block_copy)
779fc15257dSmglocker 	    (sc, 0, sc->sc_ri.ri_emuheight, dx, dy, cx, cy);
780fc15257dSmglocker 	if (r != 0)
781fc15257dSmglocker 		goto fail;
782c197399bSmglocker 
7838c7540bbSmglocker 	error = udl_cmd_send_async(sc);
7848c7540bbSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
785fc15257dSmglocker fail:
7868e027a9aSmglocker 		udl_cmd_restore_offset(sc);
7878c7540bbSmglocker 		return (EAGAIN);
7888c7540bbSmglocker 	}
789072953e3Smiod 
790c91d565bSmglocker 	/* update character backing store */
791c91d565bSmglocker 	bcopy(sc->sc_cbs + ((row * sc->sc_ri.ri_cols) + src),
792c91d565bSmglocker 	    sc->sc_cbs + ((row * sc->sc_ri.ri_cols) + dst),
793c91d565bSmglocker 	    num * sizeof(*sc->sc_cbs));
794c91d565bSmglocker 
7958c7540bbSmglocker 	return (0);
796c197399bSmglocker }
797c197399bSmglocker 
798072953e3Smiod int
799c197399bSmglocker udl_copyrows(void *cookie, int src, int dst, int num)
800c197399bSmglocker {
801c197399bSmglocker 	struct rasops_info *ri = cookie;
802c197399bSmglocker 	struct udl_softc *sc;
803fc15257dSmglocker 	int sy, dy, cx, cy, r;
8048c7540bbSmglocker 	usbd_status error;
805c197399bSmglocker 
806c197399bSmglocker 	sc = ri->ri_hw;
807c197399bSmglocker 
808c197399bSmglocker 	DPRINTF(2, "%s: %s: src=%d, dst=%d, num=%d\n",
809c197399bSmglocker 	    DN(sc), FUNC, src, dst, num);
810c197399bSmglocker 
8118e027a9aSmglocker 	udl_cmd_save_offset(sc);
8128c7540bbSmglocker 
813c197399bSmglocker 	sy = src * sc->sc_ri.ri_font->fontheight;
814c197399bSmglocker 	dy = dst * sc->sc_ri.ri_font->fontheight;
815c197399bSmglocker 	cx = sc->sc_ri.ri_emuwidth;
816c197399bSmglocker 	cy = num * sc->sc_ri.ri_font->fontheight;
817c197399bSmglocker 
818c197399bSmglocker 	/* copy row block to off-screen first to fix overlay-copy problem */
819fc15257dSmglocker 	r = (sc->udl_fb_block_copy)
820fc15257dSmglocker 	    (sc, 0, sy, 0, sc->sc_ri.ri_emuheight, cx, cy);
821fc15257dSmglocker 	if (r != 0)
822fc15257dSmglocker 		goto fail;
823c197399bSmglocker 
824c197399bSmglocker 	/* copy row block back from off-screen now */
825fc15257dSmglocker 	r = (sc->udl_fb_block_copy)
826fc15257dSmglocker 	    (sc, 0, sc->sc_ri.ri_emuheight, 0, dy, cx, cy);
827fc15257dSmglocker 	if (r != 0)
828fc15257dSmglocker 		goto fail;
829c197399bSmglocker 
8308c7540bbSmglocker 	error = udl_cmd_send_async(sc);
8318c7540bbSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
832fc15257dSmglocker fail:
8338e027a9aSmglocker 		udl_cmd_restore_offset(sc);
8348c7540bbSmglocker 		return (EAGAIN);
8358c7540bbSmglocker 	}
836072953e3Smiod 
837c91d565bSmglocker 	/* update character backing store */
838c91d565bSmglocker 	bcopy(sc->sc_cbs + (src * sc->sc_ri.ri_cols),
839c91d565bSmglocker 	    sc->sc_cbs + (dst * sc->sc_ri.ri_cols),
840c91d565bSmglocker 	    (num * sc->sc_ri.ri_cols) * sizeof(*sc->sc_cbs));
841c91d565bSmglocker 
8428c7540bbSmglocker 	return (0);
843c197399bSmglocker }
844c197399bSmglocker 
845072953e3Smiod int
846e0c3e559Sjsg udl_erasecols(void *cookie, int row, int col, int num, uint32_t attr)
847c197399bSmglocker {
848c197399bSmglocker 	struct rasops_info *ri = cookie;
849c197399bSmglocker 	struct udl_softc *sc = ri->ri_hw;
85001d017baSmglocker 	uint16_t bgc;
851c197399bSmglocker 	int fg, bg;
852fc15257dSmglocker 	int x, y, cx, cy, r;
8538c7540bbSmglocker 	usbd_status error;
854c197399bSmglocker 
855c197399bSmglocker 	sc = ri->ri_hw;
856c197399bSmglocker 
857c197399bSmglocker 	DPRINTF(2, "%s: %s: row=%d, col=%d, num=%d\n",
858c197399bSmglocker 	    DN(sc), FUNC, row, col, num);
859c197399bSmglocker 
8608e027a9aSmglocker 	udl_cmd_save_offset(sc);
8618c7540bbSmglocker 
862c197399bSmglocker 	sc->sc_ri.ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
86301d017baSmglocker 	bgc = (uint16_t)sc->sc_ri.ri_devcmap[bg];
864c197399bSmglocker 
865c197399bSmglocker 	x = col * sc->sc_ri.ri_font->fontwidth;
866c197399bSmglocker 	y = row * sc->sc_ri.ri_font->fontheight;
867c197399bSmglocker 	cx = num * sc->sc_ri.ri_font->fontwidth;
868c197399bSmglocker 	cy = sc->sc_ri.ri_font->fontheight;
869c197399bSmglocker 
870c6a68e4cSmglocker 	r = (sc->udl_fb_block_write)(sc, bgc, x, y, cx, cy);
871fc15257dSmglocker 	if (r != 0)
872fc15257dSmglocker 		goto fail;
873c197399bSmglocker 
8748c7540bbSmglocker 	error = udl_cmd_send_async(sc);
8758c7540bbSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
876fc15257dSmglocker fail:
8778e027a9aSmglocker 		udl_cmd_restore_offset(sc);
8788c7540bbSmglocker 		return (EAGAIN);
8798c7540bbSmglocker 	}
880072953e3Smiod 
881c91d565bSmglocker 	/* update character backing store */
882c91d565bSmglocker 	bzero(sc->sc_cbs + ((row * sc->sc_ri.ri_cols) + col),
883c91d565bSmglocker 	    num * sizeof(*sc->sc_cbs));
884c91d565bSmglocker 
8858c7540bbSmglocker 	return (0);
886c197399bSmglocker }
887c197399bSmglocker 
888072953e3Smiod int
889e0c3e559Sjsg udl_eraserows(void *cookie, int row, int num, uint32_t attr)
890c197399bSmglocker {
891c197399bSmglocker 	struct rasops_info *ri = cookie;
892c197399bSmglocker 	struct udl_softc *sc;
89301d017baSmglocker 	uint16_t bgc;
89401d017baSmglocker 	int fg, bg;
895fc15257dSmglocker 	int x, y, cx, cy, r;
8968c7540bbSmglocker 	usbd_status error;
897c197399bSmglocker 
898c197399bSmglocker 	sc = ri->ri_hw;
899c197399bSmglocker 
900c197399bSmglocker 	DPRINTF(2, "%s: %s: row=%d, num=%d\n", DN(sc), FUNC, row, num);
901c197399bSmglocker 
9028e027a9aSmglocker 	udl_cmd_save_offset(sc);
9038c7540bbSmglocker 
90401d017baSmglocker 	sc->sc_ri.ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
90501d017baSmglocker 	bgc = (uint16_t)sc->sc_ri.ri_devcmap[bg];
90601d017baSmglocker 
907c197399bSmglocker 	x = 0;
908c197399bSmglocker 	y = row * sc->sc_ri.ri_font->fontheight;
909c197399bSmglocker 	cx = sc->sc_ri.ri_emuwidth;
910c197399bSmglocker 	cy = num * sc->sc_ri.ri_font->fontheight;
911c197399bSmglocker 
912c6a68e4cSmglocker 	r = (sc->udl_fb_block_write)(sc, bgc, x, y, cx, cy);
913fc15257dSmglocker 	if (r != 0)
914fc15257dSmglocker 		goto fail;
915c197399bSmglocker 
9168c7540bbSmglocker 	error = udl_cmd_send_async(sc);
9178c7540bbSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
918fc15257dSmglocker fail:
9198e027a9aSmglocker 		udl_cmd_restore_offset(sc);
9208c7540bbSmglocker 		return (EAGAIN);
9218c7540bbSmglocker 	}
922072953e3Smiod 
923c91d565bSmglocker 	/* update character backing store */
924c91d565bSmglocker 	bzero(sc->sc_cbs + (row * sc->sc_ri.ri_cols),
925c91d565bSmglocker 	    (num * sc->sc_ri.ri_cols) * sizeof(*sc->sc_cbs));
926c91d565bSmglocker 
9278c7540bbSmglocker 	return (0);
928c197399bSmglocker }
929c197399bSmglocker 
930072953e3Smiod int
931e0c3e559Sjsg udl_putchar(void *cookie, int row, int col, u_int uc, uint32_t attr)
932c197399bSmglocker {
933c197399bSmglocker 	struct rasops_info *ri = cookie;
934c197399bSmglocker 	struct udl_softc *sc = ri->ri_hw;
935fc15257dSmglocker 	int r;
936c197399bSmglocker 	uint16_t fgc, bgc;
937c197399bSmglocker 	uint32_t x, y, fg, bg;
938c197399bSmglocker 
939ab5cedacSmglocker 	DPRINTF(4, "%s: %s\n", DN(sc), FUNC);
940c197399bSmglocker 
9418e027a9aSmglocker 	udl_cmd_save_offset(sc);
942fc15257dSmglocker 
943c197399bSmglocker 	sc->sc_ri.ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
944c197399bSmglocker 	fgc = (uint16_t)sc->sc_ri.ri_devcmap[fg];
945c197399bSmglocker 	bgc = (uint16_t)sc->sc_ri.ri_devcmap[bg];
946c197399bSmglocker 
947c197399bSmglocker 	x = col * ri->ri_font->fontwidth;
948c197399bSmglocker 	y = row * ri->ri_font->fontheight;
949c197399bSmglocker 
950c197399bSmglocker 	if (uc == ' ') {
951c197399bSmglocker 		/*
952c8ebe74cSjasper 		 * Writing a block for the space character instead rendering
953c197399bSmglocker 		 * it from font bits is more slim.
954c197399bSmglocker 		 */
955fc15257dSmglocker 		r = (sc->udl_fb_block_write)(sc, bgc, x, y,
956c197399bSmglocker 		    ri->ri_font->fontwidth, ri->ri_font->fontheight);
957fc15257dSmglocker 		if (r != 0)
958fc15257dSmglocker 			goto fail;
959c197399bSmglocker 	} else {
960c197399bSmglocker 		/* render a character from font bits */
961fc15257dSmglocker 		r = udl_draw_char(sc, fgc, bgc, uc, x, y);
962fc15257dSmglocker 		if (r != 0)
963fc15257dSmglocker 			goto fail;
964c197399bSmglocker 	}
965c197399bSmglocker 
966c197399bSmglocker 	/*
967c197399bSmglocker 	 * We don't call udl_cmd_send_async() here, since sending each
968c197399bSmglocker 	 * character by itself gets the performance down bad.  Instead the
969c197399bSmglocker 	 * character will be buffered until another rasops function flush
970c197399bSmglocker 	 * the buffer.
971c197399bSmglocker 	 */
972072953e3Smiod 
973c91d565bSmglocker 	/* update character backing store */
974c91d565bSmglocker 	sc->sc_cbs[(row * sc->sc_ri.ri_cols) + col] = uc;
975c91d565bSmglocker 
9768c7540bbSmglocker 	return (0);
977fc15257dSmglocker 
978fc15257dSmglocker fail:
9798e027a9aSmglocker 	udl_cmd_restore_offset(sc);
980fc15257dSmglocker 	return (EAGAIN);
981c197399bSmglocker }
982c197399bSmglocker 
983072953e3Smiod int
984c197399bSmglocker udl_do_cursor(struct rasops_info *ri)
985c197399bSmglocker {
986c197399bSmglocker 	struct udl_softc *sc = ri->ri_hw;
987c91d565bSmglocker 	int r, pos;
988c197399bSmglocker 	uint32_t x, y;
9898c7540bbSmglocker 	uint8_t save_cursor;
9908c7540bbSmglocker 	usbd_status error;
991c197399bSmglocker 
992c197399bSmglocker 	DPRINTF(2, "%s: %s: ccol=%d, crow=%d\n",
993c197399bSmglocker 	    DN(sc), FUNC, ri->ri_ccol, ri->ri_crow);
994c197399bSmglocker 
9958e027a9aSmglocker 	udl_cmd_save_offset(sc);
9968c7540bbSmglocker 	save_cursor = sc->sc_cursor_on;
9978c7540bbSmglocker 
998c197399bSmglocker 	x = ri->ri_ccol * ri->ri_font->fontwidth;
999c197399bSmglocker 	y = ri->ri_crow * ri->ri_font->fontheight;
1000c197399bSmglocker 
1001c197399bSmglocker 	if (sc->sc_cursor_on == 0) {
1002487911f5Smglocker 		/* save the last character block to off-screen */
1003fc15257dSmglocker 		r = (sc->udl_fb_block_copy)(sc, x, y, 0, sc->sc_ri.ri_emuheight,
1004c197399bSmglocker 		    ri->ri_font->fontwidth, ri->ri_font->fontheight);
1005fc15257dSmglocker 		if (r != 0)
1006fc15257dSmglocker 			goto fail;
1007c197399bSmglocker 
1008c197399bSmglocker 		/* draw cursor */
1009c91d565bSmglocker 		pos = (ri->ri_crow * sc->sc_ri.ri_cols) + ri->ri_ccol;
1010c91d565bSmglocker 		if (sc->sc_cbs[pos] == 0 || sc->sc_cbs[pos] == ' ') {
1011fc15257dSmglocker 			r = (sc->udl_fb_block_write)(sc, 0xffff, x, y,
1012c197399bSmglocker 			    ri->ri_font->fontwidth, ri->ri_font->fontheight);
1013c91d565bSmglocker 		} else {
1014c91d565bSmglocker 			r = udl_draw_char(sc, 0x0000, 0xffff, sc->sc_cbs[pos],
1015c91d565bSmglocker 			    x, y);
1016c91d565bSmglocker 		}
1017fc15257dSmglocker 		if (r != 0)
1018fc15257dSmglocker 			goto fail;
1019c197399bSmglocker 
1020c197399bSmglocker 		sc->sc_cursor_on = 1;
1021c197399bSmglocker 	} else {
1022487911f5Smglocker 		/* restore the last saved character from off-screen */
1023fc15257dSmglocker 		r = (sc->udl_fb_block_copy)(sc, 0, sc->sc_ri.ri_emuheight, x, y,
1024c197399bSmglocker 		    ri->ri_font->fontwidth, ri->ri_font->fontheight);
1025fc15257dSmglocker 		if (r != 0)
1026fc15257dSmglocker 			goto fail;
1027c197399bSmglocker 
1028c197399bSmglocker 		sc->sc_cursor_on = 0;
1029c197399bSmglocker 	}
1030c197399bSmglocker 
10318c7540bbSmglocker 	error = udl_cmd_send_async(sc);
10328c7540bbSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
1033fc15257dSmglocker fail:
10348e027a9aSmglocker 		udl_cmd_restore_offset(sc);
10358c7540bbSmglocker 		sc->sc_cursor_on = save_cursor;
10368c7540bbSmglocker 		return (EAGAIN);
10378c7540bbSmglocker 	}
1038072953e3Smiod 
10398c7540bbSmglocker 	return (0);
1040c197399bSmglocker }
1041c197399bSmglocker 
104223e0be93Smglocker int
104323e0be93Smglocker udl_draw_char(struct udl_softc *sc, uint16_t fg, uint16_t bg, u_int uc,
104423e0be93Smglocker     uint32_t x, uint32_t y)
104523e0be93Smglocker {
104623e0be93Smglocker 	int i, j, ly, r;
104723e0be93Smglocker 	uint8_t *fontchar;
104823e0be93Smglocker 	uint8_t buf[UDL_CMD_MAX_DATA_SIZE];
104923e0be93Smglocker 	uint16_t *line, lrgb16, fontbits, luc;
105023e0be93Smglocker 	struct wsdisplay_font *font = sc->sc_ri.ri_font;
105123e0be93Smglocker 
105223e0be93Smglocker 	fontchar = (uint8_t *)(font->data + (uc - font->firstchar) *
105323e0be93Smglocker 	    sc->sc_ri.ri_fontscale);
105423e0be93Smglocker 
105523e0be93Smglocker 	ly = y;
105623e0be93Smglocker 	for (i = 0; i < font->fontheight; i++) {
105723e0be93Smglocker 		if (font->fontwidth > 8) {
105823e0be93Smglocker 			fontbits = betoh16(*(uint16_t *)fontchar);
105923e0be93Smglocker 		} else {
106023e0be93Smglocker 			fontbits = *fontchar;
106123e0be93Smglocker 			fontbits = fontbits << 8;
106223e0be93Smglocker 		}
106323e0be93Smglocker 		line = (uint16_t *)buf;
106423e0be93Smglocker 
106523e0be93Smglocker 		for (j = 15; j > (15 - font->fontwidth); j--) {
106623e0be93Smglocker 			luc = 1 << j;
106723e0be93Smglocker 			if (fontbits & luc)
106823e0be93Smglocker 				lrgb16 = htobe16(fg);
106923e0be93Smglocker 			else
107023e0be93Smglocker 				lrgb16 = htobe16(bg);
107123e0be93Smglocker 			bcopy(&lrgb16, line, 2);
107223e0be93Smglocker 			line++;
107323e0be93Smglocker 		}
107423e0be93Smglocker 		r = (sc->udl_fb_buf_write)(sc, buf, x, ly, font->fontwidth);
107523e0be93Smglocker 		if (r != 0)
107623e0be93Smglocker 			return (r);
107723e0be93Smglocker 		ly++;
107823e0be93Smglocker 
107923e0be93Smglocker 		fontchar += font->stride;
108023e0be93Smglocker 	}
108123e0be93Smglocker 
108223e0be93Smglocker 	return (0);
108323e0be93Smglocker }
108423e0be93Smglocker 
1085fa695a51Smglocker int
1086fa695a51Smglocker udl_damage(struct udl_softc *sc, uint8_t *image,
1087fa695a51Smglocker     uint32_t x1, uint32_t x2, uint32_t y1, uint32_t y2)
1088fa695a51Smglocker {
1089fa695a51Smglocker 	int r;
1090fa695a51Smglocker 	int x, y, width, height;
109103b640e7Smglocker 	usbd_status error;
109203b640e7Smglocker 
10938e027a9aSmglocker 	udl_cmd_save_offset(sc);
1094fa695a51Smglocker 
1095fa695a51Smglocker 	x = x1;
1096fa695a51Smglocker 	y = y1;
1097fa695a51Smglocker 	width = x2 - x1;
1098fa695a51Smglocker 	height = y2 - y1;
1099fa695a51Smglocker 
1100fa695a51Smglocker 	r = udl_draw_image(sc, image, x, y, width, height);
1101fa695a51Smglocker 	if (r != 0)
110203b640e7Smglocker 		goto fail;
1103fa695a51Smglocker 
110403b640e7Smglocker 	error = udl_cmd_send_async(sc);
110503b640e7Smglocker 	if (error != USBD_NORMAL_COMPLETION) {
110603b640e7Smglocker fail:
11078e027a9aSmglocker 		udl_cmd_restore_offset(sc);
110803b640e7Smglocker 		return (EAGAIN);
110903b640e7Smglocker 	}
1110fa695a51Smglocker 
1111fa695a51Smglocker 	return (0);
1112fa695a51Smglocker }
1113fa695a51Smglocker 
1114fa695a51Smglocker int
1115fa695a51Smglocker udl_draw_image(struct udl_softc *sc, uint8_t *image,
1116fa695a51Smglocker     uint32_t x, uint32_t y, uint32_t width, uint32_t height)
1117fa695a51Smglocker {
1118fa695a51Smglocker 	int i, j, r;
1119fa695a51Smglocker 	int width_cur, x_cur;
1120fa695a51Smglocker 	uint8_t buf[UDL_CMD_MAX_DATA_SIZE];
1121fa695a51Smglocker 	uint16_t *image16, lrgb16;
1122fa695a51Smglocker 	uint32_t off, block;
1123fa695a51Smglocker 
1124fa695a51Smglocker 	for (i = 0; i < height; i++) {
1125fa695a51Smglocker 		off = ((y * sc->sc_width) + x) * 2;
1126fa695a51Smglocker 		x_cur = x;
1127fa695a51Smglocker 		width_cur = width;
1128fa695a51Smglocker 
1129fa695a51Smglocker 		while (width_cur) {
1130fa695a51Smglocker 			if (width_cur > UDL_CMD_MAX_PIXEL_COUNT)
1131fa695a51Smglocker 				block = UDL_CMD_MAX_PIXEL_COUNT;
1132fa695a51Smglocker 			else
1133fa695a51Smglocker 				block = width_cur;
1134fa695a51Smglocker 
1135fa695a51Smglocker 			/* fix RGB ordering */
1136fa695a51Smglocker 			image16 = (uint16_t *)(image + off);
1137fa695a51Smglocker 			for (j = 0; j < (block * 2); j += 2) {
1138fa695a51Smglocker 				lrgb16 = htobe16(*image16);
1139fa695a51Smglocker 				bcopy(&lrgb16, buf + j, 2);
1140fa695a51Smglocker 				image16++;
1141fa695a51Smglocker 			}
1142fa695a51Smglocker 
1143fa695a51Smglocker 			r = (sc->udl_fb_buf_write)(sc, buf, x_cur, y, block);
1144fa695a51Smglocker 			if (r != 0)
1145fa695a51Smglocker 				return (r);
1146fa695a51Smglocker 
1147fa695a51Smglocker 			off += block * 2;
1148fa695a51Smglocker 			x_cur += block;
1149fa695a51Smglocker 			width_cur -= block;
1150fa695a51Smglocker 		}
1151fa695a51Smglocker 		y++;
1152fa695a51Smglocker 	}
1153fa695a51Smglocker 
1154fa695a51Smglocker 	return (0);
1155fa695a51Smglocker }
1156fa695a51Smglocker 
1157c197399bSmglocker /* ---------- */
1158c197399bSmglocker 
1159c197399bSmglocker usbd_status
1160c197399bSmglocker udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r,
1161c197399bSmglocker     uint16_t index, uint16_t value, uint8_t *buf, size_t len)
1162c197399bSmglocker {
1163c197399bSmglocker 	usb_device_request_t req;
1164c197399bSmglocker 	usbd_status error;
1165c197399bSmglocker 
1166c197399bSmglocker 	req.bmRequestType = rt;
1167c197399bSmglocker 	req.bRequest = r;
1168c197399bSmglocker 	USETW(req.wIndex, index);
1169c197399bSmglocker 	USETW(req.wValue, value);
1170c197399bSmglocker 	USETW(req.wLength, len);
1171c197399bSmglocker 
1172c197399bSmglocker 	error = usbd_do_request(sc->sc_udev, &req, buf);
1173c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
1174c197399bSmglocker 		printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(error));
1175c197399bSmglocker 		return (error);
1176c197399bSmglocker 	}
1177c197399bSmglocker 
1178c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1179c197399bSmglocker }
1180c197399bSmglocker 
1181c197399bSmglocker usbd_status
1182c197399bSmglocker udl_poll(struct udl_softc *sc, uint32_t *buf)
1183c197399bSmglocker {
1184c197399bSmglocker 	uint8_t lbuf[4];
1185c197399bSmglocker 	usbd_status error;
1186c197399bSmglocker 
1187c197399bSmglocker 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
1188c197399bSmglocker 	    UDL_CTRL_CMD_POLL, 0x0000, 0x0000, lbuf, 4);
1189c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
1190c197399bSmglocker 		printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(error));
1191c197399bSmglocker 		return (error);
1192c197399bSmglocker 	}
1193c197399bSmglocker 	*buf = *(uint32_t *)lbuf;
1194c197399bSmglocker 
1195c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1196c197399bSmglocker }
1197c197399bSmglocker 
1198c197399bSmglocker usbd_status
1199c197399bSmglocker udl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf)
1200c197399bSmglocker {
1201c197399bSmglocker 	uint8_t lbuf[1];
1202c197399bSmglocker 	usbd_status error;
1203c197399bSmglocker 
1204c197399bSmglocker 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
1205c197399bSmglocker 	    UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1);
1206c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
1207c197399bSmglocker 		printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(error));
1208c197399bSmglocker 		return (error);
1209c197399bSmglocker 	}
1210c197399bSmglocker 	*buf = *(uint8_t *)lbuf;
1211c197399bSmglocker 
1212c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1213c197399bSmglocker }
1214c197399bSmglocker 
1215c197399bSmglocker usbd_status
1216c197399bSmglocker udl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf)
1217c197399bSmglocker {
1218c197399bSmglocker 	usbd_status error;
1219c197399bSmglocker 
1220c197399bSmglocker 	error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE,
1221c197399bSmglocker 	    UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1);
1222c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
1223c197399bSmglocker 		printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(error));
1224c197399bSmglocker 		return (error);
1225c197399bSmglocker 	}
1226c197399bSmglocker 
1227c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1228c197399bSmglocker }
1229c197399bSmglocker 
1230c197399bSmglocker usbd_status
1231c197399bSmglocker udl_read_edid(struct udl_softc *sc, uint8_t *buf)
1232c197399bSmglocker {
1233c197399bSmglocker 	uint8_t lbuf[64];
1234c197399bSmglocker 	uint16_t offset;
1235c197399bSmglocker 	usbd_status error;
1236c197399bSmglocker 
1237c197399bSmglocker 	offset = 0;
1238c197399bSmglocker 
1239c197399bSmglocker 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
1240c197399bSmglocker 	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64);
1241c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1242c197399bSmglocker 		goto fail;
1243c197399bSmglocker 	bcopy(lbuf + 1, buf + offset, 63);
1244c197399bSmglocker 	offset += 63;
1245c197399bSmglocker 
1246c197399bSmglocker 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
1247c197399bSmglocker 	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64);
1248c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1249c197399bSmglocker 		goto fail;
1250c197399bSmglocker 	bcopy(lbuf + 1, buf + offset, 63);
1251c197399bSmglocker 	offset += 63;
1252c197399bSmglocker 
1253c197399bSmglocker 	error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
1254c197399bSmglocker 	    UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3);
1255c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1256c197399bSmglocker 		goto fail;
1257c197399bSmglocker 	bcopy(lbuf + 1, buf + offset, 2);
1258c197399bSmglocker 
1259c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1260c197399bSmglocker fail:
1261c197399bSmglocker 	printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(error));
1262c197399bSmglocker 	return (error);
1263c197399bSmglocker }
1264c197399bSmglocker 
12656e641510Smaja uint8_t
1266af3c181fSmpi udl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t freq,
12676e641510Smaja     uint16_t chip, uint32_t clock)
12686e641510Smaja {
12696e641510Smaja 	uint8_t	idx = 0;
12706e641510Smaja 
12716e641510Smaja 	/*
12726e641510Smaja 	 * Check first if we have a matching mode with pixelclock
12736e641510Smaja 	 */
12746e641510Smaja 	while (idx < MAX_DL_MODES) {
12756e641510Smaja 		if ((udl_modes[idx].hdisplay == hdisplay) &&
12766e641510Smaja 		    (udl_modes[idx].vdisplay == vdisplay) &&
12776e641510Smaja 		    (udl_modes[idx].clock == clock) &&
12786e641510Smaja 		    (udl_modes[idx].chip <= chip)) {
12796e641510Smaja 			return(idx);
12806e641510Smaja 		}
12816e641510Smaja 		idx++;
12826e641510Smaja 	}
12836e641510Smaja 
12846e641510Smaja 	/*
12856e641510Smaja 	 * If not, check for matching mode with update frequency
12866e641510Smaja 	 */
12876e641510Smaja 	idx = 0;
12886e641510Smaja 	while (idx < MAX_DL_MODES) {
12896e641510Smaja 		if ((udl_modes[idx].hdisplay == hdisplay) &&
12906e641510Smaja 		    (udl_modes[idx].vdisplay == vdisplay) &&
1291af3c181fSmpi 		    (udl_modes[idx].freq == freq) &&
12926e641510Smaja 		    (udl_modes[idx].chip <= chip)) {
12936e641510Smaja 			return(idx);
12946e641510Smaja 		}
12956e641510Smaja 		idx++;
12966e641510Smaja 	}
129753fd709fSmglocker 
12986e641510Smaja 	return(idx);
12996e641510Smaja }
13006e641510Smaja 
13016e641510Smaja int
13026e641510Smaja udl_select_chip(struct udl_softc *sc)
13036e641510Smaja {
13046e641510Smaja 	char serialnum[USB_MAX_STRING_LEN];
13056e641510Smaja 	usb_device_descriptor_t *dd;
13066e641510Smaja 	usb_string_descriptor_t us;
13076e641510Smaja 	usbd_status error;
13086e641510Smaja 	int len, i, n;
13096e641510Smaja 	char *s;
131053fd709fSmglocker 	uint16_t c;
13116e641510Smaja 
13126e641510Smaja 	sc->sc_chip = DL120;
13136e641510Smaja 
13146e641510Smaja 	dd = usbd_get_device_descriptor(sc->sc_udev);
13156e641510Smaja 
1316825f6ef7Smaja 	if ((UGETW(dd->idVendor) == USB_VENDOR_DISPLAYLINK) &&
1317825f6ef7Smaja 	    (UGETW(dd->idProduct) == USB_PRODUCT_DISPLAYLINK_WSDVI)) {
1318825f6ef7Smaja 
1319825f6ef7Smaja 		/*
1320825f6ef7Smaja 		 * WS Tech DVI is DL120 or DL160. All deviced uses the
1321825f6ef7Smaja 		 * same revision (0.04) so iSerialNumber must be used
13224b1a56afSjsg 		 * to determine which chip it is.
1323825f6ef7Smaja 		 */
1324825f6ef7Smaja 
13256e641510Smaja 		bzero(serialnum, sizeof serialnum);
13266e641510Smaja 		error = usbd_get_string_desc(sc->sc_udev, dd->iSerialNumber,
13276e641510Smaja 		    0, &us, &len);
13286e641510Smaja 		if (error != USBD_NORMAL_COMPLETION)
13296e641510Smaja 			return (1);
13306e641510Smaja 
13316e641510Smaja 		s = &serialnum[0];
13326e641510Smaja 		n = len / 2 - 1;
1333397525a8Sjsg 		for (i = 0; i < n && i < nitems(us.bString); i++) {
13346e641510Smaja 			c = UGETW(us.bString[i]);
13356e641510Smaja 			/* Convert from Unicode, handle buggy strings. */
13366e641510Smaja 			if ((c & 0xff00) == 0)
13376e641510Smaja 				*s++ = c;
13386e641510Smaja 			else if ((c & 0x00ff) == 0)
13396e641510Smaja 				*s++ = c >> 8;
13406e641510Smaja 			else
13416e641510Smaja 				*s++ = '?';
13426e641510Smaja 		}
13436e641510Smaja 		*s++ = 0;
13446e641510Smaja 
13456e641510Smaja 		if (strlen(serialnum) > 7)
13466e641510Smaja 			if (strncmp(serialnum, "0198-13", 7) == 0)
13476e641510Smaja 				sc->sc_chip = DL160;
13486e641510Smaja 
13496e641510Smaja 		DPRINTF(1, "%s: %s: iSerialNumber (%s) used to select chip (%d)\n",
13506e641510Smaja 		     DN(sc), FUNC, serialnum, sc->sc_chip);
13516e641510Smaja 
1352825f6ef7Smaja 	}
1353825f6ef7Smaja 
1354825f6ef7Smaja 	if ((UGETW(dd->idVendor) == USB_VENDOR_DISPLAYLINK) &&
1355825f6ef7Smaja 	    (UGETW(dd->idProduct) == USB_PRODUCT_DISPLAYLINK_SWDVI)) {
1356825f6ef7Smaja 
1357825f6ef7Smaja 		/*
1358825f6ef7Smaja 		 * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision
1359825f6ef7Smaja 		 * can be used to differ between DL1x0 and DL1x5. Minor to
13604b1a56afSjsg 		 * differ between DL1x5. iSerialNumber seems not to be unique.
1361825f6ef7Smaja 		 */
1362825f6ef7Smaja 
1363825f6ef7Smaja 		sc->sc_chip = DL160;
1364825f6ef7Smaja 
1365825f6ef7Smaja 		if (UGETW(dd->bcdDevice) >= 0x100) {
1366825f6ef7Smaja 			sc->sc_chip = DL165;
1367825f6ef7Smaja 			if (UGETW(dd->bcdDevice) == 0x104)
1368825f6ef7Smaja 				sc->sc_chip = DL195;
1369825f6ef7Smaja 			if (UGETW(dd->bcdDevice) == 0x108)
1370825f6ef7Smaja 				sc->sc_chip = DL125;
1371825f6ef7Smaja 		}
1372825f6ef7Smaja 
1373825f6ef7Smaja 		DPRINTF(1, "%s: %s: bcdDevice (%02x) used to select chip (%d)\n",
1374825f6ef7Smaja 		     DN(sc), FUNC, UGETW(dd->bcdDevice), sc->sc_chip);
1375825f6ef7Smaja 
1376825f6ef7Smaja 	}
1377825f6ef7Smaja 
13786e641510Smaja 	return (0);
13796e641510Smaja }
13806e641510Smaja 
1381c197399bSmglocker usbd_status
1382c197399bSmglocker udl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len)
1383c197399bSmglocker {
1384c197399bSmglocker 	usbd_status error;
1385c197399bSmglocker 
1386c197399bSmglocker 	error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE,
1387c197399bSmglocker 	    UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len);
1388c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
1389c197399bSmglocker 		printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(error));
1390c197399bSmglocker 		return (error);
1391c197399bSmglocker 	}
1392c197399bSmglocker 
1393c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1394c197399bSmglocker }
1395c197399bSmglocker 
1396c197399bSmglocker usbd_status
1397c197399bSmglocker udl_set_decomp_table(struct udl_softc *sc, uint8_t *buf, uint16_t len)
1398c197399bSmglocker {
1399c197399bSmglocker 	int err;
1400c197399bSmglocker 
1401c197399bSmglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
1402c197399bSmglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_CMD_DECOMP);
1403c197399bSmglocker 	udl_cmd_insert_int_4(sc, 0x263871cd);	/* magic number */
1404c197399bSmglocker 	udl_cmd_insert_int_4(sc, 0x00000200);	/* 512 byte chunks */
1405c197399bSmglocker 	udl_cmd_insert_buf(sc, buf, len);
1406c197399bSmglocker 
1407c197399bSmglocker 	err = udl_cmd_send(sc);
1408c197399bSmglocker 	if (err != 0)
1409c197399bSmglocker 		return (USBD_INVAL);
1410c197399bSmglocker 
1411c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1412c197399bSmglocker }
1413c197399bSmglocker 
1414c197399bSmglocker /* ---------- */
1415c197399bSmglocker 
14162fa8dc63Smglocker int
14172fa8dc63Smglocker udl_load_huffman(struct udl_softc *sc)
14182fa8dc63Smglocker {
14192fa8dc63Smglocker 	const char *name = "udl_huffman";
14202fa8dc63Smglocker 	int error;
14212fa8dc63Smglocker 
14222fa8dc63Smglocker 	if (sc->sc_huffman == NULL) {
14232fa8dc63Smglocker 		error = loadfirmware(name, &sc->sc_huffman,
14242fa8dc63Smglocker 		    &sc->sc_huffman_size);
14252fa8dc63Smglocker 		if (error != 0) {
14262fa8dc63Smglocker 			printf("%s: error %d, could not read huffman table "
14272fa8dc63Smglocker 			    "%s!\n", DN(sc), error, name);
14282fa8dc63Smglocker 			return (EIO);
14292fa8dc63Smglocker 		}
14302fa8dc63Smglocker 	}
14312fa8dc63Smglocker 
14322fa8dc63Smglocker 	DPRINTF(1, "%s: huffman table %s allocated\n", DN(sc), name);
14332fa8dc63Smglocker 
14342fa8dc63Smglocker 	return (0);
14352fa8dc63Smglocker }
14362fa8dc63Smglocker 
14372fa8dc63Smglocker void
14382fa8dc63Smglocker udl_free_huffman(struct udl_softc *sc)
14392fa8dc63Smglocker {
14402fa8dc63Smglocker 	if (sc->sc_huffman != NULL) {
14418985a220Smglocker 		free(sc->sc_huffman, M_USBDEV, sc->sc_huffman_size);
14422fa8dc63Smglocker 		sc->sc_huffman = NULL;
14432fa8dc63Smglocker 		sc->sc_huffman_size = 0;
14442fa8dc63Smglocker 		DPRINTF(1, "%s: huffman table freed\n", DN(sc));
14452fa8dc63Smglocker 	}
14462fa8dc63Smglocker }
14472fa8dc63Smglocker 
1448fa695a51Smglocker int
1449fa695a51Smglocker udl_fbmem_alloc(struct udl_softc *sc)
1450fa695a51Smglocker {
1451fa695a51Smglocker 	int size;
1452fa695a51Smglocker 
1453fa695a51Smglocker 	size = (sc->sc_width * sc->sc_height) * (sc->sc_depth / 8);
1454fa695a51Smglocker 	size = round_page(size);
1455fa695a51Smglocker 
1456fa695a51Smglocker 	if (sc->sc_fbmem == NULL) {
14578985a220Smglocker 		sc->sc_fbmem = malloc(size, M_USBDEV, M_NOWAIT|M_ZERO);
1458fa695a51Smglocker 		if (sc->sc_fbmem == NULL)
1459fa695a51Smglocker 			return (-1);
1460fa695a51Smglocker 	}
1461c9ee9455Sderaadt 	sc->sc_fbmemsize = size;
1462fa695a51Smglocker 	return (0);
1463fa695a51Smglocker }
1464fa695a51Smglocker 
1465fa695a51Smglocker void
1466fa695a51Smglocker udl_fbmem_free(struct udl_softc *sc)
1467fa695a51Smglocker {
1468fa695a51Smglocker 	if (sc->sc_fbmem != NULL) {
14698985a220Smglocker 		free(sc->sc_fbmem, M_USBDEV, sc->sc_fbmemsize);
1470fa695a51Smglocker 		sc->sc_fbmem = NULL;
1471c9ee9455Sderaadt 		sc->sc_fbmemsize = 0;
1472fa695a51Smglocker 	}
1473fa695a51Smglocker }
1474fa695a51Smglocker 
1475c197399bSmglocker usbd_status
1476c197399bSmglocker udl_cmd_alloc_xfer(struct udl_softc *sc)
1477c197399bSmglocker {
1478c197399bSmglocker 	int i;
1479c197399bSmglocker 
1480c197399bSmglocker 	for (i = 0; i < UDL_CMD_XFER_COUNT; i++) {
1481c197399bSmglocker 		struct udl_cmd_xfer *cx = &sc->sc_cmd_xfer[i];
1482c197399bSmglocker 
1483c197399bSmglocker 		cx->sc = sc;
1484c197399bSmglocker 
1485c197399bSmglocker 		cx->xfer = usbd_alloc_xfer(sc->sc_udev);
1486c197399bSmglocker 		if (cx->xfer == NULL) {
1487c197399bSmglocker 			printf("%s: %s: can't allocate xfer handle!\n",
1488c197399bSmglocker 			    DN(sc), FUNC);
1489c197399bSmglocker 			return (USBD_NOMEM);
1490c197399bSmglocker 		}
1491c197399bSmglocker 
1492c197399bSmglocker 		cx->buf = usbd_alloc_buffer(cx->xfer, UDL_CMD_MAX_XFER_SIZE);
1493c197399bSmglocker 		if (cx->buf == NULL) {
1494c197399bSmglocker 			printf("%s: %s: can't allocate xfer buffer!\n",
1495c197399bSmglocker 			    DN(sc), FUNC);
1496c197399bSmglocker 			return (USBD_NOMEM);
1497c197399bSmglocker 		}
1498c197399bSmglocker 	}
1499c197399bSmglocker 
1500c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1501c197399bSmglocker }
1502c197399bSmglocker 
1503c197399bSmglocker void
1504c197399bSmglocker udl_cmd_free_xfer(struct udl_softc *sc)
1505c197399bSmglocker {
1506c197399bSmglocker 	int i;
1507c197399bSmglocker 
1508c197399bSmglocker 	for (i = 0; i < UDL_CMD_XFER_COUNT; i++) {
1509c197399bSmglocker 		struct udl_cmd_xfer *cx = &sc->sc_cmd_xfer[i];
1510c197399bSmglocker 
1511c197399bSmglocker 		if (cx->xfer != NULL) {
1512c197399bSmglocker 			usbd_free_xfer(cx->xfer);
1513c197399bSmglocker 			cx->xfer = NULL;
1514c197399bSmglocker 		}
1515c197399bSmglocker 	}
1516c197399bSmglocker }
1517c197399bSmglocker 
1518c197399bSmglocker int
1519c197399bSmglocker udl_cmd_alloc_buf(struct udl_softc *sc)
1520c197399bSmglocker {
1521c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1522c197399bSmglocker 
15238985a220Smglocker 	cb->buf = malloc(UDL_CMD_MAX_XFER_SIZE, M_USBDEV, M_NOWAIT|M_ZERO);
1524c197399bSmglocker 	if (cb->buf == NULL) {
1525c197399bSmglocker 		printf("%s: %s: can't allocate buffer!\n",
1526c197399bSmglocker 		    DN(sc), FUNC);
1527c197399bSmglocker 		return (ENOMEM);
1528c197399bSmglocker 	}
1529c197399bSmglocker 	cb->off = 0;
15302fa8dc63Smglocker 	cb->compblock = 0;
1531c197399bSmglocker 
1532c197399bSmglocker 	return (0);
1533c197399bSmglocker }
1534c197399bSmglocker 
1535c197399bSmglocker void
1536c197399bSmglocker udl_cmd_free_buf(struct udl_softc *sc)
1537c197399bSmglocker {
1538c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1539c197399bSmglocker 
15409fe656fbSmglocker 	if (cb->buf != NULL) {
15418985a220Smglocker 		free(cb->buf, M_USBDEV, UDL_CMD_MAX_XFER_SIZE);
15429fe656fbSmglocker 		cb->buf = NULL;
15439fe656fbSmglocker 	}
1544c197399bSmglocker 	cb->off = 0;
1545c197399bSmglocker }
1546c197399bSmglocker 
1547c197399bSmglocker void
1548c197399bSmglocker udl_cmd_insert_int_1(struct udl_softc *sc, uint8_t value)
1549c197399bSmglocker {
1550c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1551c197399bSmglocker 
1552c197399bSmglocker 	cb->buf[cb->off] = value;
1553c197399bSmglocker 
1554c197399bSmglocker 	cb->off += 1;
1555c197399bSmglocker }
1556c197399bSmglocker 
1557c197399bSmglocker void
1558c197399bSmglocker udl_cmd_insert_int_2(struct udl_softc *sc, uint16_t value)
1559c197399bSmglocker {
1560c197399bSmglocker 	uint16_t lvalue;
1561c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1562c197399bSmglocker 
1563c197399bSmglocker 	lvalue = htobe16(value);
1564a2cff4d1Smglocker 	bcopy(&lvalue, cb->buf + cb->off, 2);
1565c197399bSmglocker 
1566c197399bSmglocker 	cb->off += 2;
1567c197399bSmglocker }
1568c197399bSmglocker 
1569c197399bSmglocker void
1570c197399bSmglocker udl_cmd_insert_int_3(struct udl_softc *sc, uint32_t value)
1571c197399bSmglocker {
1572c197399bSmglocker 	uint32_t lvalue;
1573c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1574c197399bSmglocker #if BYTE_ORDER == BIG_ENDIAN
1575c197399bSmglocker 	lvalue = htobe32(value) << 8;
1576c197399bSmglocker #else
1577c197399bSmglocker 	lvalue = htobe32(value) >> 8;
1578c197399bSmglocker #endif
1579a2cff4d1Smglocker 	bcopy(&lvalue, cb->buf + cb->off, 3);
1580c197399bSmglocker 
1581c197399bSmglocker 	cb->off += 3;
1582c197399bSmglocker }
1583c197399bSmglocker 
1584c197399bSmglocker void
1585c197399bSmglocker udl_cmd_insert_int_4(struct udl_softc *sc, uint32_t value)
1586c197399bSmglocker {
1587c197399bSmglocker 	uint32_t lvalue;
1588c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1589c197399bSmglocker 
1590c197399bSmglocker 	lvalue = htobe32(value);
1591a2cff4d1Smglocker 	bcopy(&lvalue, cb->buf + cb->off, 4);
1592c197399bSmglocker 
1593c197399bSmglocker 	cb->off += 4;
1594c197399bSmglocker }
1595c197399bSmglocker 
1596c197399bSmglocker void
1597c197399bSmglocker udl_cmd_insert_buf(struct udl_softc *sc, uint8_t *buf, uint32_t len)
1598c197399bSmglocker {
1599c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1600c197399bSmglocker 
1601c197399bSmglocker 	bcopy(buf, cb->buf + cb->off, len);
1602c197399bSmglocker 
1603c197399bSmglocker 	cb->off += len;
1604c197399bSmglocker }
1605c197399bSmglocker 
16062fa8dc63Smglocker int
16072fa8dc63Smglocker udl_cmd_insert_buf_comp(struct udl_softc *sc, uint8_t *buf, uint32_t len)
16082fa8dc63Smglocker {
16092fa8dc63Smglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
16102fa8dc63Smglocker 	struct udl_huffman *h;
161128d90ff9Smglocker 	uint8_t bit_pos;
16122fa8dc63Smglocker 	uint16_t *pixels, prev;
16132fa8dc63Smglocker 	int16_t diff;
161428d90ff9Smglocker 	uint32_t bit_count, bit_pattern, bit_cur;
1615f34f9b4cSmglocker 	int i, j, bytes, eob, padding, next;
16162fa8dc63Smglocker 
16172fa8dc63Smglocker 	pixels = (uint16_t *)buf;
16182fa8dc63Smglocker 	bit_pos = bytes = eob = padding = 0;
16192fa8dc63Smglocker 
16202fa8dc63Smglocker 	/*
16212fa8dc63Smglocker 	 * If the header doesn't fit into the 512 byte main-block anymore,
16222fa8dc63Smglocker 	 * skip the header and finish up the main-block.  We return zero
16232fa8dc63Smglocker 	 * to signal our caller that the header has been skipped.
16242fa8dc63Smglocker 	 */
162529d27783Smglocker 	if (cb->compblock >= UDL_CB_RESTART_SIZE) {
16262fa8dc63Smglocker 		cb->off -= UDL_CMD_WRITE_HEAD_SIZE;
16272fa8dc63Smglocker 		cb->compblock -= UDL_CMD_WRITE_HEAD_SIZE;
16282fa8dc63Smglocker 		eob = 1;
16292fa8dc63Smglocker 	}
16302fa8dc63Smglocker 
16312fa8dc63Smglocker 	/*
16322fa8dc63Smglocker 	 * Generate a sub-block with maximal 256 pixels compressed data.
16332fa8dc63Smglocker 	 */
16342fa8dc63Smglocker 	for (i = 0; i < len / 2 && eob == 0; i++) {
16352fa8dc63Smglocker 		/* get difference between current and previous pixel */
16362fa8dc63Smglocker 		if (i > 0)
16372fa8dc63Smglocker 			prev = betoh16(pixels[i - 1]);
16382fa8dc63Smglocker 		else
16392fa8dc63Smglocker 			prev = 0;
16402fa8dc63Smglocker 
16412fa8dc63Smglocker 		/* get the huffman difference bit sequence */
16422fa8dc63Smglocker 		diff = betoh16(pixels[i]) - prev;
16432fa8dc63Smglocker 		h = (struct udl_huffman *)(sc->sc_huffman + UDL_HUFFMAN_BASE);
16442fa8dc63Smglocker 		h += diff;
16452fa8dc63Smglocker 		bit_count = h->bit_count;
16462fa8dc63Smglocker 		bit_pattern = betoh32(h->bit_pattern);
16472fa8dc63Smglocker 
1648f34f9b4cSmglocker 
1649f34f9b4cSmglocker 		/* we are near the end of the main-block, so quit loop */
1650f34f9b4cSmglocker 		if (bit_count % 8 == 0)
1651f34f9b4cSmglocker 			next = bit_count / 8;
1652f34f9b4cSmglocker 		else
1653f34f9b4cSmglocker 			next = (bit_count / 8) + 1;
1654f34f9b4cSmglocker 
1655f34f9b4cSmglocker 		if (cb->compblock + next >= UDL_CB_BODY_SIZE) {
1656f34f9b4cSmglocker 			eob = 1;
1657f34f9b4cSmglocker 			break;
1658f34f9b4cSmglocker 		}
1659f34f9b4cSmglocker 
16602fa8dc63Smglocker 		/* generate one pixel compressed data */
16612fa8dc63Smglocker 		for (j = 0; j < bit_count; j++) {
16622fa8dc63Smglocker 			if (bit_pos == 0)
16632fa8dc63Smglocker 				cb->buf[cb->off] = 0;
16642fa8dc63Smglocker 			bit_cur = (bit_pattern >> j) & 1;
16652fa8dc63Smglocker 			cb->buf[cb->off] |= (bit_cur << bit_pos);
16662fa8dc63Smglocker 			bit_pos++;
16672fa8dc63Smglocker 
16682fa8dc63Smglocker 			if (bit_pos == 8) {
16692fa8dc63Smglocker 				bit_pos = 0;
16702fa8dc63Smglocker 				cb->off++;
16712fa8dc63Smglocker 				cb->compblock++;
16722fa8dc63Smglocker 			}
16732fa8dc63Smglocker 		}
16742fa8dc63Smglocker 		bytes += 2;
16752fa8dc63Smglocker 	}
16762fa8dc63Smglocker 
16772fa8dc63Smglocker 	/*
16782fa8dc63Smglocker 	 * If we have bits left in our last byte, round up to the next
16792fa8dc63Smglocker 	 * byte, so we don't overwrite them.
16802fa8dc63Smglocker 	 */
16812fa8dc63Smglocker 	if (bit_pos != 0) {
16822fa8dc63Smglocker 		cb->off++;
16832fa8dc63Smglocker 		cb->compblock++;
16842fa8dc63Smglocker 	}
16852fa8dc63Smglocker 
16862fa8dc63Smglocker 	/*
16872fa8dc63Smglocker 	 * Finish up a 512 byte main-block.  The leftover space gets
1688c8ebe74cSjasper 	 * padded to zero.  Finally terminate the block by writing the
16892fa8dc63Smglocker 	 * 0xff-into-UDL_REG_SYNC-register sequence.
16902fa8dc63Smglocker 	 */
16912fa8dc63Smglocker 	if (eob == 1) {
16922fa8dc63Smglocker 		padding = (UDL_CB_BODY_SIZE - cb->compblock);
16932fa8dc63Smglocker 		for (i = 0; i < padding; i++) {
16942fa8dc63Smglocker 			cb->buf[cb->off] = 0;
16952fa8dc63Smglocker 			cb->off++;
16962fa8dc63Smglocker 			cb->compblock++;
16972fa8dc63Smglocker 		}
16982fa8dc63Smglocker 		udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
16992fa8dc63Smglocker 		cb->compblock = 0;
17002fa8dc63Smglocker 	}
17012fa8dc63Smglocker 
17022fa8dc63Smglocker 	/* return how many bytes we have compressed */
17032fa8dc63Smglocker 	return (bytes);
17042fa8dc63Smglocker }
17052fa8dc63Smglocker 
17062fa8dc63Smglocker int
17072fa8dc63Smglocker udl_cmd_insert_head_comp(struct udl_softc *sc, uint32_t len)
17082fa8dc63Smglocker {
17092fa8dc63Smglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
17102fa8dc63Smglocker 	int i, padding;
17112fa8dc63Smglocker 
1712f34f9b4cSmglocker 	if (cb->compblock > UDL_CB_BODY_SIZE) {
17132fa8dc63Smglocker 		cb->off -= UDL_CMD_COPY_HEAD_SIZE;
17142fa8dc63Smglocker 		cb->compblock -= UDL_CMD_COPY_HEAD_SIZE;
17152fa8dc63Smglocker 
17162fa8dc63Smglocker 		padding = (UDL_CB_BODY_SIZE - cb->compblock);
17172fa8dc63Smglocker 		for (i = 0; i < padding; i++) {
17182fa8dc63Smglocker 			cb->buf[cb->off] = 0;
17192fa8dc63Smglocker 			cb->off++;
17202fa8dc63Smglocker 			cb->compblock++;
17212fa8dc63Smglocker 		}
17222fa8dc63Smglocker 		udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
17232fa8dc63Smglocker 		cb->compblock = 0;
17242fa8dc63Smglocker 		return (0);
17252fa8dc63Smglocker 	}
17262fa8dc63Smglocker 
17272fa8dc63Smglocker 	return (len);
17282fa8dc63Smglocker }
17292fa8dc63Smglocker 
1730fc15257dSmglocker int
1731fc15257dSmglocker udl_cmd_insert_check(struct udl_softc *sc, int len)
1732c197399bSmglocker {
1733fc15257dSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1734c197399bSmglocker 	int total;
1735fc15257dSmglocker 	usbd_status error;
1736c197399bSmglocker 
1737c197399bSmglocker 	total = cb->off + len;
1738c197399bSmglocker 
1739fc15257dSmglocker 	if (total > UDL_CMD_MAX_XFER_SIZE) {
1740fc15257dSmglocker 		/* command buffer is almost full, try to flush it */
1741b1107686Smglocker 		if (cb->xfer_type == UDL_CMD_XFER_ASYNC)
1742fc15257dSmglocker 			error = udl_cmd_send_async(sc);
1743fc15257dSmglocker 		else
1744fc15257dSmglocker 			error = udl_cmd_send(sc);
1745fc15257dSmglocker 		if (error != USBD_NORMAL_COMPLETION) {
1746fc15257dSmglocker 			DPRINTF(1, "%s: %s: can't flush full command buffer\n",
1747fc15257dSmglocker 			    DN(sc), FUNC);
1748fc15257dSmglocker 			return (EAGAIN);
1749c197399bSmglocker 		}
1750c197399bSmglocker 	}
1751c197399bSmglocker 
1752fc15257dSmglocker 	return (0);
1753fc15257dSmglocker }
1754fc15257dSmglocker 
1755fc15257dSmglocker void
1756b1107686Smglocker udl_cmd_set_xfer_type(struct udl_softc *sc, int xfer_type)
1757fc15257dSmglocker {
1758fc15257dSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1759fc15257dSmglocker 
1760b1107686Smglocker 	cb->xfer_type = xfer_type;
1761fc15257dSmglocker }
1762fc15257dSmglocker 
17633a801951Smglocker void
17648e027a9aSmglocker udl_cmd_save_offset(struct udl_softc *sc)
17658c7540bbSmglocker {
17668c7540bbSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
17678c7540bbSmglocker 
17683a801951Smglocker 	cb->off_save = cb->off;
17693a801951Smglocker 	cb->compblock_save = cb->compblock;
17708c7540bbSmglocker }
17718c7540bbSmglocker 
17728c7540bbSmglocker void
17738e027a9aSmglocker udl_cmd_restore_offset(struct udl_softc *sc)
17748c7540bbSmglocker {
17758c7540bbSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
17768c7540bbSmglocker 
17773a801951Smglocker 	cb->off = cb->off_save;
17783a801951Smglocker 	cb->compblock = cb->compblock_save;
17798c7540bbSmglocker }
17808c7540bbSmglocker 
1781c197399bSmglocker void
1782c197399bSmglocker udl_cmd_write_reg_1(struct udl_softc *sc, uint8_t reg, uint8_t val)
1783c197399bSmglocker {
1784c197399bSmglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
1785c197399bSmglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_CMD_REG_WRITE_1);
1786c197399bSmglocker 	udl_cmd_insert_int_1(sc, reg);
1787c197399bSmglocker 	udl_cmd_insert_int_1(sc, val);
1788c197399bSmglocker }
1789c197399bSmglocker 
1790c197399bSmglocker void
1791c197399bSmglocker udl_cmd_write_reg_3(struct udl_softc *sc, uint8_t reg, uint32_t val)
1792c197399bSmglocker {
1793c197399bSmglocker 	udl_cmd_write_reg_1(sc, reg + 0, (val >> 16) & 0xff);
1794c197399bSmglocker 	udl_cmd_write_reg_1(sc, reg + 1, (val >> 8) & 0xff);
1795c197399bSmglocker 	udl_cmd_write_reg_1(sc, reg + 2, (val >> 0) & 0xff);
1796c197399bSmglocker }
1797c197399bSmglocker 
1798c197399bSmglocker usbd_status
1799c197399bSmglocker udl_cmd_send(struct udl_softc *sc)
1800c197399bSmglocker {
1801c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1802c197399bSmglocker 	struct udl_cmd_xfer *cx = &sc->sc_cmd_xfer[0];
1803c197399bSmglocker 	int len;
1804c197399bSmglocker 	usbd_status error;
1805c197399bSmglocker 
1806c197399bSmglocker 	/* mark end of command stack */
1807c197399bSmglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
1808c197399bSmglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_CMD_EOC);
1809c197399bSmglocker 
1810c197399bSmglocker 	bcopy(cb->buf, cx->buf, cb->off);
1811c197399bSmglocker 
1812c197399bSmglocker 	len = cb->off;
1813431119e5Smpi 	usbd_setup_xfer(cx->xfer, sc->sc_tx_pipeh, 0, cx->buf, len,
1814431119e5Smpi 	    USBD_NO_COPY | USBD_SHORT_XFER_OK | USBD_SYNCHRONOUS, 1000, NULL);
1815431119e5Smpi 	error = usbd_transfer(cx->xfer);
1816c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION) {
1817c197399bSmglocker 		printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(error));
1818c197399bSmglocker 		/* we clear our buffer now to avoid growing out of bounds */
1819c197399bSmglocker 		goto fail;
1820c197399bSmglocker 	}
1821c197399bSmglocker 	DPRINTF(1, "%s: %s: sent %d of %d bytes\n",
1822c197399bSmglocker 	    DN(sc), FUNC, len, cb->off);
1823c197399bSmglocker fail:
1824c197399bSmglocker 	cb->off = 0;
18252fa8dc63Smglocker 	cb->compblock = 0;
1826c197399bSmglocker 
1827c197399bSmglocker 	return (error);
1828c197399bSmglocker }
1829c197399bSmglocker 
1830c197399bSmglocker usbd_status
1831c197399bSmglocker udl_cmd_send_async(struct udl_softc *sc)
1832c197399bSmglocker {
1833c197399bSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
1834c197399bSmglocker 	struct udl_cmd_xfer *cx;
1835c197399bSmglocker 	usbd_status error;
1836c197399bSmglocker 	int i, s;
1837c197399bSmglocker 
183823e0be93Smglocker 	/* check if command xfer queue is full */
18398c7540bbSmglocker 	if (sc->sc_cmd_xfer_cnt == UDL_CMD_XFER_COUNT)
18408c7540bbSmglocker 		return (USBD_IN_USE);
1841c197399bSmglocker 
1842c197399bSmglocker 	s = splusb();	/* no callbacks please until accounting is done */
1843c197399bSmglocker 
184423e0be93Smglocker 	/* find a free command xfer buffer */
1845c197399bSmglocker 	for (i = 0; i < UDL_CMD_XFER_COUNT; i++) {
1846c197399bSmglocker 		if (sc->sc_cmd_xfer[i].busy == 0)
1847c197399bSmglocker 			break;
1848c197399bSmglocker 	}
1849c197399bSmglocker 	if (i == UDL_CMD_XFER_COUNT) {
1850a3f1f5adSmglocker 		/* this shouldn't happen */
1851a8359cd7Sjsg 		splx(s);
1852a3f1f5adSmglocker 		return (USBD_IN_USE);
1853c197399bSmglocker 	}
1854c197399bSmglocker 	cx = &sc->sc_cmd_xfer[i];
1855c197399bSmglocker 
1856dba6ed21Smglocker 	/* mark end of command stack */
1857dba6ed21Smglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
1858dba6ed21Smglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_CMD_EOC);
1859dba6ed21Smglocker 
1860c197399bSmglocker 	/* copy command buffer to xfer buffer */
1861c197399bSmglocker 	bcopy(cb->buf, cx->buf, cb->off);
1862c197399bSmglocker 
1863c197399bSmglocker 	/* do xfer */
1864c197399bSmglocker 	usbd_setup_xfer(cx->xfer, sc->sc_tx_pipeh, cx, cx->buf, cb->off,
1865c197399bSmglocker 	     USBD_NO_COPY, 1000, udl_cmd_send_async_cb);
1866c197399bSmglocker 	error = usbd_transfer(cx->xfer);
1867c197399bSmglocker 	if (error != 0 && error != USBD_IN_PROGRESS) {
1868c197399bSmglocker 		printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(error));
18698c7540bbSmglocker 		splx(s);
1870c197399bSmglocker 		return (error);
1871c197399bSmglocker 	}
187214a356fdSmglocker 	DPRINTF(2, "%s: %s: sending %d bytes from buffer no. %d\n",
1873c197399bSmglocker 	    DN(sc), FUNC, cb->off, i);
1874c197399bSmglocker 
1875c197399bSmglocker 	/* free command buffer, lock xfer buffer */
1876c197399bSmglocker 	cb->off = 0;
18772fa8dc63Smglocker 	cb->compblock = 0;
1878c197399bSmglocker 	cx->busy = 1;
1879c197399bSmglocker 	sc->sc_cmd_xfer_cnt++;
1880c197399bSmglocker 
1881c197399bSmglocker 	splx(s);
1882c197399bSmglocker 
1883c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1884c197399bSmglocker }
1885c197399bSmglocker 
1886c197399bSmglocker void
1887ab0b1be7Smglocker udl_cmd_send_async_cb(struct usbd_xfer *xfer, void *priv, usbd_status status)
1888c197399bSmglocker {
1889c197399bSmglocker 	struct udl_cmd_xfer *cx = priv;
1890c197399bSmglocker 	struct udl_softc *sc = cx->sc;
1891c197399bSmglocker 	int len;
1892c197399bSmglocker 
1893c197399bSmglocker 	if (status != USBD_NORMAL_COMPLETION) {
1894c197399bSmglocker 		printf("%s: %s: %s!\n", DN(sc), FUNC, usbd_errstr(status));
1895c197399bSmglocker 
1896c197399bSmglocker 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
1897c197399bSmglocker 			return;
1898c197399bSmglocker 		if (status == USBD_STALLED)
1899c197399bSmglocker 			usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh);
1900c197399bSmglocker 		goto skip;
1901c197399bSmglocker 	}
1902c197399bSmglocker 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
1903c197399bSmglocker 
1904ab5cedacSmglocker 	DPRINTF(3, "%s: %s: sent %d bytes\n", DN(sc), FUNC, len);
1905c197399bSmglocker skip:
1906c197399bSmglocker 	/* free xfer buffer */
1907c197399bSmglocker 	cx->busy = 0;
1908c197399bSmglocker 	sc->sc_cmd_xfer_cnt--;
1909f4611ff0Smglocker 
1910f4611ff0Smglocker 	/* wakeup UDLIO_DAMAGE if it sleeps for a free xfer buffer */
1911f4611ff0Smglocker 	wakeup(sc);
1912c197399bSmglocker }
1913c197399bSmglocker 
1914c197399bSmglocker /* ---------- */
1915c197399bSmglocker 
1916c197399bSmglocker usbd_status
1917c197399bSmglocker udl_init_chip(struct udl_softc *sc)
1918c197399bSmglocker {
1919c197399bSmglocker 	uint8_t ui8;
1920c197399bSmglocker 	uint32_t ui32;
1921c197399bSmglocker 	usbd_status error;
1922c197399bSmglocker 
1923c197399bSmglocker 	error = udl_poll(sc, &ui32);
1924c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1925c197399bSmglocker 		return (error);
1926c197399bSmglocker 	DPRINTF(1, "%s: %s: poll=0x%08x\n", DN(sc), FUNC, ui32);
1927c197399bSmglocker 
1928825f6ef7Smaja 	/* Some products may use later chip too */
1929825f6ef7Smaja 	switch (ui32 & 0xff) {
1930825f6ef7Smaja 	case 0xf1:				/* DL1x5 */
1931825f6ef7Smaja 		switch (sc->sc_chip) {
1932825f6ef7Smaja 		case DL120:
1933825f6ef7Smaja 			sc->sc_chip = DL125;
1934825f6ef7Smaja 			break;
1935825f6ef7Smaja 		case DL160:
1936825f6ef7Smaja 			sc->sc_chip = DL165;
1937825f6ef7Smaja 			break;
1938825f6ef7Smaja 		}
1939825f6ef7Smaja 		break;
1940825f6ef7Smaja 	}
1941825f6ef7Smaja 	DPRINTF(1, "%s: %s: chip %d\n", DN(sc), FUNC, sc->sc_chip);
1942825f6ef7Smaja 
1943c197399bSmglocker 	error = udl_read_1(sc, 0xc484, &ui8);
1944c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1945c197399bSmglocker 		return (error);
1946c197399bSmglocker 	DPRINTF(1, "%s: %s: read 0x%02x from 0xc484\n", DN(sc), FUNC, ui8);
1947c197399bSmglocker 
1948c197399bSmglocker 	error = udl_write_1(sc, 0xc41f, 0x01);
1949c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1950c197399bSmglocker 		return (error);
1951c197399bSmglocker 	DPRINTF(1, "%s: %s: write 0x01 to 0xc41f\n", DN(sc), FUNC);
1952c197399bSmglocker 
19536e641510Smaja 	error = udl_read_edid(sc, sc->sc_edid);
1954c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1955c197399bSmglocker 		return (error);
19566e641510Smaja 	DPRINTF(1, "%s: %s: read EDID\n", DN(sc), FUNC);
19576e641510Smaja 
1958c197399bSmglocker 	error = udl_set_enc_key(sc, udl_null_key_1, sizeof(udl_null_key_1));
1959c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1960c197399bSmglocker 		return (error);
1961c197399bSmglocker 	DPRINTF(1, "%s: %s: set encryption key\n", DN(sc), FUNC);
1962c197399bSmglocker 
1963c197399bSmglocker 	error = udl_write_1(sc, 0xc40b, 0x00);
1964c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1965c197399bSmglocker 		return (error);
196636a555e0Smglocker 	DPRINTF(1, "%s: %s: write 0x00 to 0xc40b\n", DN(sc), FUNC);
1967c197399bSmglocker 
1968c197399bSmglocker 	error = udl_set_decomp_table(sc, udl_decomp_table,
1969c197399bSmglocker 	    sizeof(udl_decomp_table));
1970c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
1971c197399bSmglocker 		return (error);
1972c197399bSmglocker 	DPRINTF(1, "%s: %s: set decompression table\n", DN(sc), FUNC);
1973c197399bSmglocker 
1974c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
1975c197399bSmglocker }
1976c197399bSmglocker 
1977c197399bSmglocker void
1978c197399bSmglocker udl_init_fb_offsets(struct udl_softc *sc, uint32_t start16, uint32_t stride16,
1979c197399bSmglocker     uint32_t start8, uint32_t stride8)
1980c197399bSmglocker {
1981c197399bSmglocker 	udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0x00);
1982c197399bSmglocker 	udl_cmd_write_reg_3(sc, UDL_REG_ADDR_START16, start16);
1983c197399bSmglocker 	udl_cmd_write_reg_3(sc, UDL_REG_ADDR_STRIDE16, stride16);
1984c197399bSmglocker 	udl_cmd_write_reg_3(sc, UDL_REG_ADDR_START8, start8);
1985c197399bSmglocker 	udl_cmd_write_reg_3(sc, UDL_REG_ADDR_STRIDE8, stride8);
1986c197399bSmglocker 	udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
1987c197399bSmglocker }
1988c197399bSmglocker 
1989c197399bSmglocker usbd_status
19906e641510Smaja udl_init_resolution(struct udl_softc *sc)
1991c197399bSmglocker {
1992c197399bSmglocker 	int i;
1993c197399bSmglocker 	usbd_status error;
19946e641510Smaja 	uint8_t *buf = udl_modes[sc->sc_cur_mode].mode;
1995c197399bSmglocker 
1996c197399bSmglocker 	/* write resolution values and set video memory offsets */
1997c197399bSmglocker 	udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0x00);
19986e641510Smaja 	for (i = 0; i < UDL_MODE_SIZE; i++)
1999c197399bSmglocker 		udl_cmd_write_reg_1(sc, i, buf[i]);
2000c197399bSmglocker 	udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
2001c197399bSmglocker 
2002c197399bSmglocker 	udl_init_fb_offsets(sc, 0x000000, 0x000a00, 0x555555, 0x000500);
2003c197399bSmglocker 	error = udl_cmd_send(sc);
2004c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
2005c197399bSmglocker 		return (error);
2006c197399bSmglocker 
2007c197399bSmglocker 	/* clear screen */
2008082da39dSmglocker 	error = udl_clear_screen(sc);
2009c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
2010c197399bSmglocker 		return (error);
2011c197399bSmglocker 
2012c197399bSmglocker 	/* show framebuffer content */
201392fdbf9fSmglocker 	udl_cmd_write_reg_1(sc, UDL_REG_SCREEN, UDL_REG_SCREEN_ON);
2014c197399bSmglocker 	udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
2015c197399bSmglocker 	error = udl_cmd_send(sc);
2016c197399bSmglocker 	if (error != USBD_NORMAL_COMPLETION)
2017c197399bSmglocker 		return (error);
2018c197399bSmglocker 
2019c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
2020c197399bSmglocker }
2021c197399bSmglocker 
2022082da39dSmglocker usbd_status
2023082da39dSmglocker udl_clear_screen(struct udl_softc *sc)
2024082da39dSmglocker {
2025082da39dSmglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
2026082da39dSmglocker 	usbd_status error;
2027082da39dSmglocker 
2028082da39dSmglocker 	/* clear screen */
2029082da39dSmglocker 	udl_fb_block_write(sc, 0x0000, 0, 0, sc->sc_width, sc->sc_height);
2030082da39dSmglocker 	if (cb->xfer_type == UDL_CMD_XFER_ASYNC)
2031082da39dSmglocker 		error = udl_cmd_send_async(sc);
2032082da39dSmglocker 	else
2033082da39dSmglocker 		error = udl_cmd_send(sc);
2034082da39dSmglocker 	if (error != USBD_NORMAL_COMPLETION)
2035082da39dSmglocker 		return (error);
2036082da39dSmglocker 
2037082da39dSmglocker 	return (USBD_NORMAL_COMPLETION);
2038082da39dSmglocker }
2039082da39dSmglocker 
20406e641510Smaja void
20416e641510Smaja udl_select_mode(struct udl_softc *sc)
20426e641510Smaja {
20436e641510Smaja 	struct udl_mode mode;
20446e641510Smaja 	int index = MAX_DL_MODES, i;
20456e641510Smaja 
20466e641510Smaja 	/* try to get the preferred mode from EDID */
20470c9d3924Smiod 	edid_parse(DN(sc), sc->sc_edid, &sc->sc_edid_info);
2048c07d3a0fSmiod #if defined(UDL_DEBUG) && defined(EDID_DEBUG)
20496e641510Smaja 	edid_print(&sc->sc_edid_info);
20506e641510Smaja #endif
20516e641510Smaja 	if (sc->sc_edid_info.edid_preferred_mode != NULL) {
2052af3c181fSmpi 		mode.freq =
20536e641510Smaja 		    (sc->sc_edid_info.edid_preferred_mode->dot_clock * 1000) /
20546e641510Smaja 		    (sc->sc_edid_info.edid_preferred_mode->htotal *
20556e641510Smaja 		     sc->sc_edid_info.edid_preferred_mode->vtotal);
20566e641510Smaja 		mode.clock =
20576e641510Smaja 		    sc->sc_edid_info.edid_preferred_mode->dot_clock / 10;
20586e641510Smaja 		mode.hdisplay =
20596e641510Smaja 		    sc->sc_edid_info.edid_preferred_mode->hdisplay;
20606e641510Smaja 		mode.vdisplay =
20616e641510Smaja 		    sc->sc_edid_info.edid_preferred_mode->vdisplay;
2062af3c181fSmpi 		index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, mode.freq,
20636e641510Smaja 		    sc->sc_chip, mode.clock);
20646e641510Smaja 		sc->sc_cur_mode = index;
20656e641510Smaja 	} else {
20666e641510Smaja 		DPRINTF(1, "%s: %s: no preferred mode found!\n", DN(sc), FUNC);
20676e641510Smaja 	}
20686e641510Smaja 
20696e641510Smaja 	if (index == MAX_DL_MODES) {
20706e641510Smaja 		DPRINTF(1, "%s: %s: no mode line found for %dx%d @ %dHz!\n",
2071af3c181fSmpi 		    DN(sc), FUNC, mode.hdisplay, mode.vdisplay, mode.freq);
20726e641510Smaja 
20736e641510Smaja 		i = 0;
20746e641510Smaja 		while (i < sc->sc_edid_info.edid_nmodes) {
2075af3c181fSmpi 			mode.freq =
20766e641510Smaja 			    (sc->sc_edid_info.edid_modes[i].dot_clock * 1000) /
20776e641510Smaja 			    (sc->sc_edid_info.edid_modes[i].htotal *
20786e641510Smaja 			     sc->sc_edid_info.edid_modes[i].vtotal);
20796e641510Smaja 			mode.clock =
20806e641510Smaja 			    sc->sc_edid_info.edid_modes[i].dot_clock / 10;
20816e641510Smaja 			mode.hdisplay =
20826e641510Smaja 			    sc->sc_edid_info.edid_modes[i].hdisplay;
20836e641510Smaja 			mode.vdisplay =
20846e641510Smaja 			    sc->sc_edid_info.edid_modes[i].vdisplay;
20856e641510Smaja 			index = udl_lookup_mode(mode.hdisplay, mode.vdisplay,
2086af3c181fSmpi 			    mode.freq, sc->sc_chip, mode.clock);
20876e641510Smaja 			if (index < MAX_DL_MODES)
20886e641510Smaja 				if ((sc->sc_cur_mode == MAX_DL_MODES) ||
20896e641510Smaja 				    (index > sc->sc_cur_mode))
20906e641510Smaja 					sc->sc_cur_mode = index;
20916e641510Smaja 			i++;
20926e641510Smaja 		}
20936e641510Smaja 	}
20946e641510Smaja 
20956e641510Smaja 	/*
209653fd709fSmglocker 	 * If no mode found use default.
20976e641510Smaja 	 */
209853fd709fSmglocker 	if (sc->sc_cur_mode == MAX_DL_MODES)
20996e641510Smaja 		sc->sc_cur_mode = udl_lookup_mode(800, 600, 60, sc->sc_chip, 0);
21006e641510Smaja 
21016e641510Smaja 	mode = udl_modes[sc->sc_cur_mode];
21026e641510Smaja 	sc->sc_width = mode.hdisplay;
21036e641510Smaja 	sc->sc_height = mode.vdisplay;
21046e641510Smaja 
21056e641510Smaja 	/*
210653fd709fSmglocker 	 * We always use 16bit color depth for now.
21076e641510Smaja 	 */
21086e641510Smaja 	sc->sc_depth = 16;
21096e641510Smaja 
21106e641510Smaja 	DPRINTF(1, "%s: %s: %dx%d @ %dHz\n",
2111af3c181fSmpi 	    DN(sc), FUNC, mode.hdisplay, mode.vdisplay, mode.freq);
21126e641510Smaja }
21136e641510Smaja 
2114fc15257dSmglocker int
211523e0be93Smglocker udl_fb_buf_write(struct udl_softc *sc, uint8_t *buf, uint32_t x,
211623e0be93Smglocker     uint32_t y, uint16_t width)
2117c197399bSmglocker {
211823e0be93Smglocker 	uint16_t lwidth;
211923e0be93Smglocker 	uint32_t off;
212023e0be93Smglocker 	int r;
2121fc15257dSmglocker 
2122fc15257dSmglocker 	r = udl_cmd_insert_check(sc, UDL_CMD_WRITE_MAX_SIZE);
2123fc15257dSmglocker 	if (r != 0)
2124fc15257dSmglocker 		return (r);
2125c197399bSmglocker 
212623e0be93Smglocker 	off = ((y * sc->sc_width) + x) * 2;
2127c197399bSmglocker 	lwidth = width * 2;
2128c197399bSmglocker 
2129c197399bSmglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
2130c197399bSmglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD);
213123e0be93Smglocker 	udl_cmd_insert_int_3(sc, off);
2132b1f23cbdSmglocker 	udl_cmd_insert_int_1(sc, width >= UDL_CMD_MAX_PIXEL_COUNT ? 0 : width);
2133c197399bSmglocker 
213423e0be93Smglocker 	udl_cmd_insert_buf(sc, buf, lwidth);
213523e0be93Smglocker 
213623e0be93Smglocker 	return (0);
2137c197399bSmglocker }
2138c197399bSmglocker 
213923e0be93Smglocker int
214023e0be93Smglocker udl_fb_block_write(struct udl_softc *sc, uint16_t rgb16, uint32_t x,
214123e0be93Smglocker     uint32_t y, uint32_t width, uint32_t height)
214223e0be93Smglocker {
214323e0be93Smglocker 	uint32_t i;
214423e0be93Smglocker 	int r;
214523e0be93Smglocker 
214623e0be93Smglocker 	for (i = 0; i < height; i++) {
214723e0be93Smglocker 		r = udl_fb_line_write(sc, rgb16, x, y + i, width);
214823e0be93Smglocker 		if (r != 0)
214923e0be93Smglocker 			return (r);
215023e0be93Smglocker 	}
2151fc15257dSmglocker 
2152fc15257dSmglocker 	return (0);
2153c197399bSmglocker }
2154c197399bSmglocker 
2155fc15257dSmglocker int
21562c2babadSmglocker udl_fb_line_write(struct udl_softc *sc, uint16_t rgb16, uint32_t x,
2157c197399bSmglocker     uint32_t y, uint32_t width)
2158c197399bSmglocker {
2159c197399bSmglocker 	uint32_t off, block;
2160fc15257dSmglocker 	int r;
2161c197399bSmglocker 
2162c197399bSmglocker 	off = (y * sc->sc_width) + x;
2163c197399bSmglocker 
2164c197399bSmglocker 	while (width) {
2165c197399bSmglocker 		if (width > UDL_CMD_MAX_PIXEL_COUNT)
2166c197399bSmglocker 			block = UDL_CMD_MAX_PIXEL_COUNT;
2167c197399bSmglocker 		else
2168c197399bSmglocker 			block = width;
2169c197399bSmglocker 
2170fc15257dSmglocker 		r = udl_fb_off_write(sc, rgb16, off, block);
2171fc15257dSmglocker 		if (r != 0)
2172fc15257dSmglocker 			return (r);
2173c197399bSmglocker 
2174c197399bSmglocker 		off += block;
2175c197399bSmglocker 		width -= block;
2176c197399bSmglocker 	}
2177fc15257dSmglocker 
2178fc15257dSmglocker 	return (0);
2179c197399bSmglocker }
2180c197399bSmglocker 
2181fc15257dSmglocker int
218223e0be93Smglocker udl_fb_off_write(struct udl_softc *sc, uint16_t rgb16, uint32_t off,
218323e0be93Smglocker     uint16_t width)
2184c197399bSmglocker {
218523e0be93Smglocker 	uint8_t buf[UDL_CMD_MAX_DATA_SIZE];
218623e0be93Smglocker 	uint16_t lwidth, lrgb16;
218723e0be93Smglocker 	uint32_t loff;
218823e0be93Smglocker 	int i, r;
2189fc15257dSmglocker 
2190fc15257dSmglocker 	r = udl_cmd_insert_check(sc, UDL_CMD_WRITE_MAX_SIZE);
2191fc15257dSmglocker 	if (r != 0)
2192fc15257dSmglocker 		return (r);
21934be1f5e5Smglocker 
219423e0be93Smglocker 	loff = off * 2;
21954be1f5e5Smglocker 	lwidth = width * 2;
21964be1f5e5Smglocker 
21974be1f5e5Smglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
21984be1f5e5Smglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD);
219923e0be93Smglocker 	udl_cmd_insert_int_3(sc, loff);
22004be1f5e5Smglocker 	udl_cmd_insert_int_1(sc, width >= UDL_CMD_MAX_PIXEL_COUNT ? 0 : width);
22014be1f5e5Smglocker 
220223e0be93Smglocker 	for (i = 0; i < lwidth; i += 2) {
220323e0be93Smglocker 		lrgb16 = htobe16(rgb16);
220423e0be93Smglocker 		bcopy(&lrgb16, buf + i, 2);
220523e0be93Smglocker 	}
220623e0be93Smglocker 
22074be1f5e5Smglocker 	udl_cmd_insert_buf(sc, buf, lwidth);
2208fc15257dSmglocker 
2209fc15257dSmglocker 	return (0);
22104be1f5e5Smglocker }
22114be1f5e5Smglocker 
2212fc15257dSmglocker int
221323e0be93Smglocker udl_fb_block_copy(struct udl_softc *sc, uint32_t src_x, uint32_t src_y,
221423e0be93Smglocker     uint32_t dst_x, uint32_t dst_y, uint32_t width, uint32_t height)
2215c197399bSmglocker {
221623e0be93Smglocker 	int i, r;
2217fc15257dSmglocker 
221823e0be93Smglocker 	for (i = 0; i < height; i++) {
221923e0be93Smglocker 		r = udl_fb_line_copy(sc, src_x, src_y + i, dst_x, dst_y + i,
222023e0be93Smglocker 		    width);
2221fc15257dSmglocker 		if (r != 0)
2222fc15257dSmglocker 			return (r);
222323e0be93Smglocker 	}
2224fc15257dSmglocker 
2225fc15257dSmglocker 	return (0);
2226c197399bSmglocker }
2227c197399bSmglocker 
222823e0be93Smglocker 
2229fc15257dSmglocker int
22302c2babadSmglocker udl_fb_line_copy(struct udl_softc *sc, uint32_t src_x, uint32_t src_y,
2231c197399bSmglocker     uint32_t dst_x, uint32_t dst_y, uint32_t width)
2232c197399bSmglocker {
2233c197399bSmglocker 	uint32_t src_off, dst_off, block;
2234fc15257dSmglocker 	int r;
2235c197399bSmglocker 
2236c197399bSmglocker 	src_off = (src_y * sc->sc_width) + src_x;
2237c197399bSmglocker 	dst_off = (dst_y * sc->sc_width) + dst_x;
2238c197399bSmglocker 
2239c197399bSmglocker 	while (width) {
2240c197399bSmglocker 		if (width > UDL_CMD_MAX_PIXEL_COUNT)
2241c197399bSmglocker 			block = UDL_CMD_MAX_PIXEL_COUNT;
2242c197399bSmglocker 		else
2243c197399bSmglocker 			block = width;
2244c197399bSmglocker 
2245fc15257dSmglocker 		r = udl_fb_off_copy(sc, src_off, dst_off, block);
2246fc15257dSmglocker 		if (r != 0)
2247fc15257dSmglocker 			return (r);
2248c197399bSmglocker 
2249c197399bSmglocker 		src_off += block;
2250c197399bSmglocker 		dst_off += block;
2251c197399bSmglocker 		width -= block;
2252c197399bSmglocker 	}
2253fc15257dSmglocker 
2254fc15257dSmglocker 	return (0);
2255c197399bSmglocker }
2256c197399bSmglocker 
2257fc15257dSmglocker int
225823e0be93Smglocker udl_fb_off_copy(struct udl_softc *sc, uint32_t src_off, uint32_t dst_off,
225923e0be93Smglocker     uint16_t width)
2260c197399bSmglocker {
226123e0be93Smglocker 	uint32_t ldst_off, lsrc_off;
226223e0be93Smglocker 	int r;
2263c197399bSmglocker 
226423e0be93Smglocker 	r = udl_cmd_insert_check(sc, UDL_CMD_COPY_MAX_SIZE);
2265fc15257dSmglocker 	if (r != 0)
2266fc15257dSmglocker 		return (r);
226723e0be93Smglocker 
226823e0be93Smglocker 	ldst_off = dst_off * 2;
226923e0be93Smglocker 	lsrc_off = src_off * 2;
227023e0be93Smglocker 
227123e0be93Smglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
227223e0be93Smglocker 	udl_cmd_insert_int_1(sc, UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD);
227323e0be93Smglocker 	udl_cmd_insert_int_3(sc, ldst_off);
227423e0be93Smglocker 	udl_cmd_insert_int_1(sc, width >= UDL_CMD_MAX_PIXEL_COUNT ? 0 : width);
227523e0be93Smglocker 	udl_cmd_insert_int_3(sc, lsrc_off);
227623e0be93Smglocker 
227723e0be93Smglocker 	return (0);
227823e0be93Smglocker }
227923e0be93Smglocker 
228023e0be93Smglocker int
228123e0be93Smglocker udl_fb_buf_write_comp(struct udl_softc *sc, uint8_t *buf, uint32_t x,
228223e0be93Smglocker     uint32_t y, uint16_t width)
228323e0be93Smglocker {
228423e0be93Smglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
228523e0be93Smglocker 	uint8_t *count;
228623e0be93Smglocker 	uint16_t lwidth;
228723e0be93Smglocker 	uint32_t off;
228823e0be93Smglocker 	int r, sent;
228923e0be93Smglocker 
229023e0be93Smglocker 	r = udl_cmd_insert_check(sc, UDL_CMD_WRITE_MAX_SIZE);
229123e0be93Smglocker 	if (r != 0)
229223e0be93Smglocker 		return (r);
229323e0be93Smglocker 
229423e0be93Smglocker 	off = ((y * sc->sc_width) + x) * 2;
229523e0be93Smglocker 	lwidth = width * 2;
229623e0be93Smglocker 
229723e0be93Smglocker 	/*
229823e0be93Smglocker 	 * A new compressed stream needs the 0xff-into-UDL_REG_SYNC-register
229923e0be93Smglocker 	 * sequence always as first command.
230023e0be93Smglocker 	 */
230123e0be93Smglocker 	if (cb->off == 0)
230223e0be93Smglocker 		udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
230323e0be93Smglocker 
230423e0be93Smglocker 	r = sent = 0;
230523e0be93Smglocker 	while (sent < lwidth) {
230623e0be93Smglocker 		udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
230723e0be93Smglocker 		udl_cmd_insert_int_1(sc,
230823e0be93Smglocker 		    UDL_BULK_CMD_FB_WRITE |
230923e0be93Smglocker 		    UDL_BULK_CMD_FB_WORD |
231023e0be93Smglocker 		    UDL_BULK_CMD_FB_COMP);
231123e0be93Smglocker 		udl_cmd_insert_int_3(sc, off + sent);
231223e0be93Smglocker 		udl_cmd_insert_int_1(sc,
231323e0be93Smglocker 		    width >= UDL_CMD_MAX_PIXEL_COUNT ? 0 : width);
231423e0be93Smglocker 		cb->compblock += UDL_CMD_WRITE_HEAD_SIZE;
231523e0be93Smglocker 
231623e0be93Smglocker 		count = &cb->buf[cb->off - 1];
231723e0be93Smglocker 		r = udl_cmd_insert_buf_comp(sc, buf + sent, lwidth - sent);
231823e0be93Smglocker 		if (r > 0 && r != (lwidth - sent)) {
231923e0be93Smglocker 			*count = r / 2;
232023e0be93Smglocker 			width -= r / 2;
232123e0be93Smglocker 		}
232223e0be93Smglocker 		sent += r;
232323e0be93Smglocker 	}
232423e0be93Smglocker 
232523e0be93Smglocker 	return (0);
232623e0be93Smglocker }
232723e0be93Smglocker 
232823e0be93Smglocker int
232923e0be93Smglocker udl_fb_block_write_comp(struct udl_softc *sc, uint16_t rgb16, uint32_t x,
233023e0be93Smglocker     uint32_t y, uint32_t width, uint32_t height)
233123e0be93Smglocker {
233223e0be93Smglocker 	uint32_t i;
233323e0be93Smglocker 	int r;
233423e0be93Smglocker 
233523e0be93Smglocker 	for (i = 0; i < height; i++) {
233623e0be93Smglocker 		r = udl_fb_line_write_comp(sc, rgb16, x, y + i, width);
233723e0be93Smglocker 		if (r != 0)
233823e0be93Smglocker 			return (r);
233923e0be93Smglocker 	}
234023e0be93Smglocker 
234123e0be93Smglocker 	return (0);
234223e0be93Smglocker }
234323e0be93Smglocker 
234423e0be93Smglocker int
234523e0be93Smglocker udl_fb_line_write_comp(struct udl_softc *sc, uint16_t rgb16, uint32_t x,
234623e0be93Smglocker     uint32_t y, uint32_t width)
234723e0be93Smglocker {
234823e0be93Smglocker 	uint32_t off, block;
234923e0be93Smglocker 	int r;
235023e0be93Smglocker 
235123e0be93Smglocker 	off = (y * sc->sc_width) + x;
235223e0be93Smglocker 
235323e0be93Smglocker 	while (width) {
235423e0be93Smglocker 		if (width > UDL_CMD_MAX_PIXEL_COUNT)
235523e0be93Smglocker 			block = UDL_CMD_MAX_PIXEL_COUNT;
235623e0be93Smglocker 		else
235723e0be93Smglocker 			block = width;
235823e0be93Smglocker 
2359034172a0Smglocker 		r = udl_fb_off_write_comp(sc, rgb16, off, block);
236023e0be93Smglocker 		if (r != 0)
236123e0be93Smglocker 			return (r);
236223e0be93Smglocker 
236323e0be93Smglocker 		off += block;
236423e0be93Smglocker 		width -= block;
2365c197399bSmglocker 	}
2366c197399bSmglocker 
2367fc15257dSmglocker 	return (0);
2368fc15257dSmglocker }
2369fc15257dSmglocker 
2370fc15257dSmglocker int
23712fa8dc63Smglocker udl_fb_off_write_comp(struct udl_softc *sc, uint16_t rgb16, uint32_t off,
23722fa8dc63Smglocker     uint16_t width)
23732fa8dc63Smglocker {
23742fa8dc63Smglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
23752fa8dc63Smglocker 	uint8_t buf[UDL_CMD_MAX_DATA_SIZE];
23762fa8dc63Smglocker 	uint8_t *count;
23772fa8dc63Smglocker 	uint16_t lwidth, lrgb16;
23782fa8dc63Smglocker 	uint32_t loff;
23792fa8dc63Smglocker 	int i, r, sent;
23802fa8dc63Smglocker 
2381fc15257dSmglocker 	r = udl_cmd_insert_check(sc, UDL_CMD_WRITE_MAX_SIZE);
2382fc15257dSmglocker 	if (r != 0)
2383fc15257dSmglocker 		return (r);
2384fc15257dSmglocker 
23852fa8dc63Smglocker 	loff = off * 2;
23862fa8dc63Smglocker 	lwidth = width * 2;
23872fa8dc63Smglocker 
23882fa8dc63Smglocker 	for (i = 0; i < lwidth; i += 2) {
23892fa8dc63Smglocker 		lrgb16 = htobe16(rgb16);
2390a2cff4d1Smglocker 		bcopy(&lrgb16, buf + i, 2);
23912fa8dc63Smglocker 	}
23922fa8dc63Smglocker 
23932fa8dc63Smglocker 	/*
23942fa8dc63Smglocker 	 * A new compressed stream needs the 0xff-into-UDL_REG_SYNC-register
23952fa8dc63Smglocker 	 * sequence always as first command.
23962fa8dc63Smglocker 	 */
23972fa8dc63Smglocker 	if (cb->off == 0)
23982fa8dc63Smglocker 		udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
23992fa8dc63Smglocker 
24002fa8dc63Smglocker 	r = sent = 0;
24012fa8dc63Smglocker 	while (sent < lwidth) {
24022fa8dc63Smglocker 		udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
24032fa8dc63Smglocker 		udl_cmd_insert_int_1(sc,
24042fa8dc63Smglocker 		    UDL_BULK_CMD_FB_WRITE |
24052fa8dc63Smglocker 		    UDL_BULK_CMD_FB_WORD |
24062fa8dc63Smglocker 		    UDL_BULK_CMD_FB_COMP);
24072fa8dc63Smglocker 		udl_cmd_insert_int_3(sc, loff + sent);
24082fa8dc63Smglocker 		udl_cmd_insert_int_1(sc,
24092fa8dc63Smglocker 		    width >= UDL_CMD_MAX_PIXEL_COUNT ? 0 : width);
24102fa8dc63Smglocker 		cb->compblock += UDL_CMD_WRITE_HEAD_SIZE;
24112fa8dc63Smglocker 
24122fa8dc63Smglocker 		count = &cb->buf[cb->off - 1];
24132fa8dc63Smglocker 		r = udl_cmd_insert_buf_comp(sc, buf + sent, lwidth - sent);
24142fa8dc63Smglocker 		if (r > 0 && r != (lwidth - sent)) {
24152fa8dc63Smglocker 			*count = r / 2;
24162fa8dc63Smglocker 			width -= r / 2;
24172fa8dc63Smglocker 		}
24182fa8dc63Smglocker 		sent += r;
24192fa8dc63Smglocker 	}
2420fc15257dSmglocker 
2421fc15257dSmglocker 	return (0);
24222fa8dc63Smglocker }
24232fa8dc63Smglocker 
2424fc15257dSmglocker int
242523e0be93Smglocker udl_fb_block_copy_comp(struct udl_softc *sc, uint32_t src_x, uint32_t src_y,
242623e0be93Smglocker     uint32_t dst_x, uint32_t dst_y, uint32_t width, uint32_t height)
24272fa8dc63Smglocker {
242823e0be93Smglocker 	int i, r;
242923e0be93Smglocker 
243023e0be93Smglocker 	for (i = 0; i < height; i++) {
243123e0be93Smglocker 		r = udl_fb_line_copy_comp(sc, src_x, src_y + i,
243223e0be93Smglocker 		    dst_x, dst_y + i, width);
243323e0be93Smglocker 		if (r != 0)
243423e0be93Smglocker 			return (r);
243523e0be93Smglocker 	}
243623e0be93Smglocker 
243723e0be93Smglocker 	return (0);
243823e0be93Smglocker }
243923e0be93Smglocker 
244023e0be93Smglocker int
244123e0be93Smglocker udl_fb_line_copy_comp(struct udl_softc *sc, uint32_t src_x, uint32_t src_y,
244223e0be93Smglocker     uint32_t dst_x, uint32_t dst_y, uint32_t width)
244323e0be93Smglocker {
244423e0be93Smglocker 	uint32_t src_off, dst_off, block;
2445fc15257dSmglocker 	int r;
24462fa8dc63Smglocker 
244723e0be93Smglocker 	src_off = (src_y * sc->sc_width) + src_x;
244823e0be93Smglocker 	dst_off = (dst_y * sc->sc_width) + dst_x;
24492fa8dc63Smglocker 
24502fa8dc63Smglocker 	while (width) {
24512fa8dc63Smglocker 		if (width > UDL_CMD_MAX_PIXEL_COUNT)
24522fa8dc63Smglocker 			block = UDL_CMD_MAX_PIXEL_COUNT;
24532fa8dc63Smglocker 		else
24542fa8dc63Smglocker 			block = width;
24552fa8dc63Smglocker 
245623e0be93Smglocker 		r = udl_fb_off_copy_comp(sc, src_off, dst_off, block);
2457fc15257dSmglocker 		if (r != 0)
2458fc15257dSmglocker 			return (r);
24592fa8dc63Smglocker 
246023e0be93Smglocker 		src_off += block;
246123e0be93Smglocker 		dst_off += block;
24622fa8dc63Smglocker 		width -= block;
24632fa8dc63Smglocker 	}
2464fc15257dSmglocker 
2465fc15257dSmglocker 	return (0);
24662fa8dc63Smglocker }
24672fa8dc63Smglocker 
2468fc15257dSmglocker int
24692fa8dc63Smglocker udl_fb_off_copy_comp(struct udl_softc *sc, uint32_t src_off, uint32_t dst_off,
24702fa8dc63Smglocker     uint16_t width)
24712fa8dc63Smglocker {
24722fa8dc63Smglocker 	struct udl_cmd_buf *cb = &sc->sc_cmd_buf;
24732fa8dc63Smglocker 	uint32_t ldst_off, lsrc_off;
24742fa8dc63Smglocker 	int r;
24752fa8dc63Smglocker 
2476fc15257dSmglocker 	r = udl_cmd_insert_check(sc, UDL_CMD_COPY_MAX_SIZE);
2477fc15257dSmglocker 	if (r != 0)
2478fc15257dSmglocker 		return (r);
2479fc15257dSmglocker 
24802fa8dc63Smglocker 	ldst_off = dst_off * 2;
24812fa8dc63Smglocker 	lsrc_off = src_off * 2;
24822fa8dc63Smglocker 
24832fa8dc63Smglocker 	/*
24842fa8dc63Smglocker 	 * A new compressed stream needs the 0xff-into-UDL_REG_SYNC-register
24852fa8dc63Smglocker 	 * sequence always as first command.
24862fa8dc63Smglocker 	 */
24872fa8dc63Smglocker 	if (cb->off == 0)
24882fa8dc63Smglocker 		udl_cmd_write_reg_1(sc, UDL_REG_SYNC, 0xff);
24892fa8dc63Smglocker 
24902fa8dc63Smglocker 	r = 0;
24912fa8dc63Smglocker 	while (r < 1) {
24922fa8dc63Smglocker 		udl_cmd_insert_int_1(sc, UDL_BULK_SOC);
24932fa8dc63Smglocker 		udl_cmd_insert_int_1(sc,
24942fa8dc63Smglocker 		    UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD);
24952fa8dc63Smglocker 		udl_cmd_insert_int_3(sc, ldst_off);
24962fa8dc63Smglocker 		udl_cmd_insert_int_1(sc,
24972fa8dc63Smglocker 		    width >= UDL_CMD_MAX_PIXEL_COUNT ? 0 : width);
24982fa8dc63Smglocker 		udl_cmd_insert_int_3(sc, lsrc_off);
24992fa8dc63Smglocker 		cb->compblock += UDL_CMD_COPY_HEAD_SIZE;
25002fa8dc63Smglocker 
25012fa8dc63Smglocker 		r = udl_cmd_insert_head_comp(sc, UDL_CMD_COPY_HEAD_SIZE);
25022fa8dc63Smglocker 	}
2503fc15257dSmglocker 
2504fc15257dSmglocker 	return (0);
25052fa8dc63Smglocker }
25062fa8dc63Smglocker 
2507c197399bSmglocker /* ---------- */
2508c197399bSmglocker #ifdef UDL_DEBUG
2509c197399bSmglocker void
2510c197399bSmglocker udl_hexdump(void *buf, int len, int quiet)
2511c197399bSmglocker {
2512c197399bSmglocker 	int i;
2513c197399bSmglocker 
2514c197399bSmglocker 	for (i = 0; i < len; i++) {
2515c197399bSmglocker 		if (quiet == 0) {
2516c197399bSmglocker 			if (i % 16 == 0)
2517c197399bSmglocker 				printf("%s%5i:", i ? "\n" : "", i);
2518c197399bSmglocker 			if (i % 4 == 0)
2519c197399bSmglocker 				printf(" ");
2520c197399bSmglocker 		}
2521c197399bSmglocker 		printf("%02x", (int)*((u_char *)buf + i));
2522c197399bSmglocker 	}
2523c197399bSmglocker 	printf("\n");
2524c197399bSmglocker }
2525c197399bSmglocker 
2526c197399bSmglocker usbd_status
2527c197399bSmglocker udl_init_test(struct udl_softc *sc)
2528c197399bSmglocker {
2529c197399bSmglocker 	int i, j, parts, loops;
2530c197399bSmglocker 	uint16_t color;
2531c197399bSmglocker 	uint16_t rgb24[3] = { 0xf800, 0x07e0, 0x001f };
2532c197399bSmglocker 
2533b1f23cbdSmglocker 	loops = (sc->sc_width * sc->sc_height) / UDL_CMD_MAX_PIXEL_COUNT;
2534c197399bSmglocker 	parts = loops / 3;
2535c197399bSmglocker 	color = rgb24[0];
2536c197399bSmglocker 
2537c197399bSmglocker 	j = 1;
2538c197399bSmglocker 	for (i = 0; i < loops; i++) {
2539c197399bSmglocker 		if (i == parts) {
2540c197399bSmglocker 			color = rgb24[j];
2541c197399bSmglocker 			parts += parts;
2542c197399bSmglocker 			j++;
2543c197399bSmglocker 		}
25442fa8dc63Smglocker 		(sc->udl_fb_off_write)(sc, color, i * UDL_CMD_MAX_PIXEL_COUNT,
2545c197399bSmglocker 		    UDL_CMD_MAX_PIXEL_COUNT);
2546c197399bSmglocker 	}
2547c197399bSmglocker 	(void)udl_cmd_send(sc);
2548c197399bSmglocker 
2549c197399bSmglocker 	return (USBD_NORMAL_COMPLETION);
2550c197399bSmglocker }
2551c197399bSmglocker #endif
2552