1/* $NetBSD: udl.c,v 1.17 2016/10/18 20:17:37 nat Exp $ */
2
3/*-
4 * Copyright (c) 2009 FUKAUMI Naoki.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/*
29 * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org>
30 *
31 * Permission to use, copy, modify, and distribute this software for any
32 * purpose with or without fee is hereby granted, provided that the above
33 * copyright notice and this permission notice appear in all copies.
34 *
35 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
36 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
37 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
38 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
39 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
40 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
41 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42 */
43
44/*
45 * Driver for the ``DisplayLink DL-1x0 / DL-1x5'' graphic chips based
46 * on the reversed engineered specifications of Florian Echtler
47 * <floe at butterbrot dot org>:
48 *
49 * http://floe.butterbrot.org/displaylink/doku.php
50 *
51 * This driver was written by Marcus Glocker for OpenBSD and ported to
52 * NetBSD by FUKAUMI Naoki with many modification.
53 */
54
55#include <sys/cdefs.h>
56__KERNEL_RCSID(0, "$NetBSD: udl.c,v 1.17 2016/10/18 20:17:37 nat Exp $");
57
58#include <sys/param.h>
59#include <sys/device.h>
60#include <sys/kernel.h>
61#include <sys/proc.h>
62#include <sys/systm.h>
63#include <sys/kmem.h>
64#include <sys/kthread.h>
65#include <sys/condvar.h>
66#include <uvm/uvm.h>
67
68#include <sys/bus.h>
69#include <sys/endian.h>
70
71#include <dev/usb/usb.h>
72#include <dev/usb/usbdi.h>
73#include <dev/usb/usbdivar.h>
74#include <dev/usb/usbdi_util.h>
75#include <dev/usb/usb_mem.h>
76#include <dev/usb/usbdevs.h>
77
78#include <dev/firmload.h>
79
80#include <dev/videomode/videomode.h>
81#include <dev/videomode/edidvar.h>
82
83#include <dev/wscons/wsconsio.h>
84#include <dev/wscons/wsdisplayvar.h>
85#include <dev/rasops/rasops.h>
86
87#include <dev/usb/udl.h>
88#ifdef notyet
89#include <dev/usb/udlio.h>
90#endif
91
92/*
93 * Defines.
94 */
95#ifdef UDL_DEBUG
96#define DPRINTF(x) do { if (udl_debug) printf x; } while (0)
97#define DPRINTFN(n, x) do { if (udl_debug >= (n)) printf x; } while (0)
98int udl_debug = 1;
99#else
100#define DPRINTF(x) do {} while (0)
101#define DPRINTFN(n, x) do {} while (0)
102#endif
103
104/*
105 * Prototypes.
106 */
107static int udl_match(device_t, cfdata_t, void *);
108static void udl_attach(device_t, device_t, void *);
109static int udl_detach(device_t, int);
110
111static int udl_ioctl(void *, void *, u_long, void *, int,
112 struct lwp *);
113static paddr_t udl_mmap(void *, void *, off_t, int);
114static int udl_alloc_screen(void *, const struct wsscreen_descr *,
115 void **, int *, int *, long *);
116static void udl_free_screen(void *, void *);
117static int udl_show_screen(void *, void *, int,
118 void (*)(void *, int, int), void *);
119
120static void udl_comp_load(struct udl_softc *);
121static void udl_comp_unload(struct udl_softc *);
122static int udl_fbmem_alloc(struct udl_softc *);
123static void udl_fbmem_free(struct udl_softc *);
124static int udl_cmdq_alloc(struct udl_softc *);
125static void udl_cmdq_free(struct udl_softc *);
126static struct udl_cmdq *udl_cmdq_get(struct udl_softc *sc);
127static void udl_cmdq_put(struct udl_softc *sc,
128 struct udl_cmdq *cmdq);
129static void udl_cmdq_flush(struct udl_softc *);
130
131static void udl_cursor(void *, int, int, int);
132static void udl_putchar(void *, int, int, u_int, long);
133static void udl_copycols(void *, int, int, int, int);
134static void udl_erasecols(void *, int, int, int, long);
135static void udl_copyrows(void *, int, int, int);
136static void udl_eraserows(void *, int, int, long);
137
138static void udl_restore_char(struct rasops_info *);
139static void udl_draw_char(struct rasops_info *, uint16_t *, u_int,
140 int, int);
141static void udl_copy_rect(struct udl_softc *, int, int, int, int,
142 int, int);
143static void udl_fill_rect(struct udl_softc *, uint16_t, int, int,
144 int, int);
145#ifdef notyet
146static void udl_draw_rect(struct udl_softc *,
147 struct udl_ioctl_damage *);
148static void udl_draw_rect_comp(struct udl_softc *,
149 struct udl_ioctl_damage *);
150#endif
151
152static inline void udl_copy_line(struct udl_softc *, int, int, int);
153static inline void udl_fill_line(struct udl_softc *, uint16_t, int, int);
154static inline void udl_draw_line(struct udl_softc *, uint16_t *, int,
155 int);
156#ifdef notyet
157static inline void udl_draw_line_comp(struct udl_softc *, uint16_t *, int,
158 int);
159#endif
160
161static int udl_cmd_send(struct udl_softc *);
162static void udl_cmd_send_async(struct udl_softc *);
163static void udl_cmd_send_async_cb(struct usbd_xfer *,
164 void *, usbd_status);
165
166static int udl_ctrl_msg(struct udl_softc *, uint8_t, uint8_t,
167 uint16_t, uint16_t, uint8_t *, uint16_t);
168static int udl_init(struct udl_softc *);
169static void udl_read_edid(struct udl_softc *);
170static void udl_set_address(struct udl_softc *, int, int, int,
171 int);
172static void udl_blank(struct udl_softc *, int);
173static uint16_t udl_lfsr(uint16_t);
174static int udl_set_resolution(struct udl_softc *,
175 const struct videomode *);
176static const struct videomode *udl_videomode_lookup(const char *);
177static void udl_update_thread(void *);
178static inline void udl_startstop(struct udl_softc *, bool);
179
180static inline void
181udl_cmd_add_1(struct udl_softc *sc, uint8_t val)
182{
183
184 *sc->sc_cmd_buf++ = val;
185}
186
187static inline void
188udl_cmd_add_2(struct udl_softc *sc, uint16_t val)
189{
190
191 be16enc(sc->sc_cmd_buf, val);
192 sc->sc_cmd_buf += 2;
193}
194
195static inline void
196udl_cmd_add_3(struct udl_softc *sc, uint32_t val)
197{
198
199 udl_cmd_add_2(sc, val >> 8);
200 udl_cmd_add_1(sc, val);
201}
202
203static inline void
204udl_cmd_add_4(struct udl_softc *sc, uint32_t val)
205{
206
207 be32enc(sc->sc_cmd_buf, val);
208 sc->sc_cmd_buf += 4;
209}
210
211static inline void
212udl_cmd_add_buf(struct udl_softc *sc, uint16_t *buf, int width)
213{
214#if BYTE_ORDER == BIG_ENDIAN
215 memcpy(sc->sc_cmd_buf, buf, width * 2);
216 sc->sc_cmd_buf += width * 2;
217#else
218 uint16_t *endp;
219
220 endp = buf + width;
221
222 if (((uintptr_t)sc->sc_cmd_buf & 1) == 0) {
223 while (buf < endp) {
224 *(uint16_t *)sc->sc_cmd_buf = htobe16(*buf++);
225 sc->sc_cmd_buf += 2;
226 }
227 } else {
228 while (buf < endp) {
229 be16enc(sc->sc_cmd_buf, *buf++);
230 sc->sc_cmd_buf += 2;
231 }
232 }
233#endif
234}
235
236static inline void
237udl_reg_write_1(struct udl_softc *sc, uint8_t reg, uint8_t val)
238{
239
240 udl_cmd_add_4(sc, (UDL_BULK_SOC << 24) |
241 (UDL_BULK_CMD_REG_WRITE_1 << 16) | (reg << 8) | val);
242}
243
244static inline void
245udl_reg_write_2(struct udl_softc *sc, uint8_t reg, uint16_t val)
246{
247
248 udl_reg_write_1(sc, reg++, val >> 8);
249 udl_reg_write_1(sc, reg, val);
250}
251
252static inline void
253udl_reg_write_3(struct udl_softc *sc, uint8_t reg, uint32_t val)
254{
255
256 udl_reg_write_1(sc, reg++, val >> 16);
257 udl_reg_write_1(sc, reg++, val >> 8);
258 udl_reg_write_1(sc, reg, val);
259}
260
261/* XXX */
262static int
263firmware_load(const char *dname, const char *iname, uint8_t **ucodep,
264 size_t *sizep)
265{
266 firmware_handle_t fh;
267 int error;
268
269 if ((error = firmware_open(dname, iname, &fh)) != 0)
270 return error;
271 *sizep = firmware_get_size(fh);
272 if ((*ucodep = firmware_malloc(*sizep)) == NULL) {
273 firmware_close(fh);
274 return ENOMEM;
275 }
276 if ((error = firmware_read(fh, 0, *ucodep, *sizep)) != 0)
277 firmware_free(*ucodep, *sizep);
278 firmware_close(fh);
279
280 return error;
281}
282
283/*
284 * Driver glue.
285 */
286CFATTACH_DECL_NEW(udl, sizeof(struct udl_softc),
287 udl_match, udl_attach, udl_detach, NULL);
288
289/*
290 * wsdisplay glue.
291 */
292static struct wsdisplay_accessops udl_accessops = {
293 udl_ioctl,
294 udl_mmap,
295 udl_alloc_screen,
296 udl_free_screen,
297 udl_show_screen,
298 NULL,
299 NULL,
300 NULL,
301};
302
303/*
304 * Matching devices.
305 */
306static const struct usb_devno udl_devs[] = {
307 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020 },
308 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220 },
309 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD190 },
310 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_U70 },
311 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2 },
312 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60 },
313 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV },
314 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI },
315 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_USBRGB },
316 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCDUSB7X },
317 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCDUSB10X },
318 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10 },
319 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI },
320 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008 },
321 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GXDVIU2 },
322 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GXDVIU2B },
323 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U },
324 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U },
325 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK },
326 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571 },
327 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061 },
328 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK },
329 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI },
330 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70 },
331 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000UD_DVI },
332 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LDEWX015U },
333 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_MIMO },
334 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE },
335 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421WIDE },
336 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SD_U2VDH },
337 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0 },
338 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_FYDVI },
339 { USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_FYDVI2 }
340};
341
342static int
343udl_match(device_t parent, cfdata_t match, void *aux)
344{
345 struct usb_attach_arg *uaa = aux;
346
347 if (usb_lookup(udl_devs, uaa->uaa_vendor, uaa->uaa_product) != NULL)
348 return UMATCH_VENDOR_PRODUCT;
349
350 return UMATCH_NONE;
351}
352
353static void
354udl_attach(device_t parent, device_t self, void *aux)
355{
356 struct udl_softc *sc = device_private(self);
357 struct usb_attach_arg *uaa = aux;
358 struct wsemuldisplaydev_attach_args aa;
359 const struct videomode *vmp;
360 usbd_status error;
361 char *devinfop;
362
363 aprint_naive("\n");
364 aprint_normal("\n");
365
366 sc->sc_dev = self;
367 sc->sc_udev = uaa->uaa_device;
368
369 devinfop = usbd_devinfo_alloc(sc->sc_udev, 0);
370 aprint_normal_dev(sc->sc_dev, "%s\n", devinfop);
371 usbd_devinfo_free(devinfop);
372
373 /*
374 * Set device configuration descriptor number.
375 */
376 error = usbd_set_config_no(sc->sc_udev, 1, 0);
377 if (error != USBD_NORMAL_COMPLETION) {
378 aprint_error_dev(self, "failed to set configuration"
379 ", err=%s\n", usbd_errstr(error));
380 return;
381 }
382
383 /*
384 * Create device handle to interface descriptor.
385 */
386 error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface);
387 if (error != USBD_NORMAL_COMPLETION)
388 return;
389
390 /*
391 * Open bulk TX pipe.
392 */
393 error = usbd_open_pipe(sc->sc_iface, 1, USBD_EXCLUSIVE_USE,
394 &sc->sc_tx_pipeh);
395 if (error != USBD_NORMAL_COMPLETION)
396 return;
397
398 /*
399 * Allocate bulk command queue.
400 */
401#ifdef UDL_EVENT_COUNTERS
402 evcnt_attach_dynamic(&sc->sc_ev_cmdq_get, EVCNT_TYPE_MISC, NULL,
403 device_xname(sc->sc_dev), "udl_cmdq_get");
404 evcnt_attach_dynamic(&sc->sc_ev_cmdq_put, EVCNT_TYPE_MISC, NULL,
405 device_xname(sc->sc_dev), "udl_cmdq_put");
406 evcnt_attach_dynamic(&sc->sc_ev_cmdq_wait, EVCNT_TYPE_MISC, NULL,
407 device_xname(sc->sc_dev), "udl_cmdq_wait");
408 evcnt_attach_dynamic(&sc->sc_ev_cmdq_timeout, EVCNT_TYPE_MISC, NULL,
409 device_xname(sc->sc_dev), "udl_cmdq_timeout");
410#endif
411
412 if (udl_cmdq_alloc(sc) != 0)
413 return;
414
415 cv_init(&sc->sc_cv, device_xname(sc->sc_dev));
416 mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_TTY); /* XXX for tty_lock */
417
418 if ((sc->sc_cmd_cur = udl_cmdq_get(sc)) == NULL)
419 return;
420 UDL_CMD_BUFINIT(sc);
421
422 /*
423 * Initialize chip.
424 */
425 if (udl_init(sc) != 0)
426 return;
427
428 udl_read_edid(sc);
429
430 /*
431 * Initialize resolution.
432 */
433#ifndef UDL_VIDEOMODE
434 if (sc->sc_ei.edid_nmodes != 0 &&
435 sc->sc_ei.edid_preferred_mode != NULL)
436 vmp = sc->sc_ei.edid_preferred_mode;
437 else
438#define UDL_VIDEOMODE "640x480x60"
439#endif
440 vmp = udl_videomode_lookup(UDL_VIDEOMODE);
441
442 if (vmp == NULL)
443 return;
444
445 sc->sc_width = vmp->hdisplay;
446 sc->sc_height = vmp->vdisplay;
447 sc->sc_offscreen = sc->sc_height * 3 / 2;
448 sc->sc_depth = 16;
449
450 if (udl_set_resolution(sc, vmp) != 0)
451 return;
452
453 sc->sc_defaultscreen.name = "default";
454 sc->sc_screens[0] = &sc->sc_defaultscreen;
455 sc->sc_screenlist.nscreens = 1;
456 sc->sc_screenlist.screens = sc->sc_screens;
457
458 /*
459 * Set initial wsdisplay emulation mode.
460 */
461 sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
462
463 /*
464 * Attach wsdisplay.
465 */
466 aa.console = 0;
467 aa.scrdata = &sc->sc_screenlist;
468 aa.accessops = &udl_accessops;
469 aa.accesscookie = sc;
470
471 sc->sc_wsdisplay =
472 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint);
473
474 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
475
476 mutex_init(&sc->sc_thread_mtx, MUTEX_DEFAULT, IPL_NONE);
477 cv_init(&sc->sc_thread_cv, "udlcv");
478 sc->sc_dying = false;
479 sc->sc_thread_stop = true;
480 kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL,
481 udl_update_thread, sc, &sc->sc_thread, "udlupd");
482}
483
484static int
485udl_detach(device_t self, int flags)
486{
487 struct udl_softc *sc = device_private(self);
488
489 /*
490 * Close bulk TX pipe.
491 */
492 if (sc->sc_tx_pipeh != NULL) {
493 usbd_abort_pipe(sc->sc_tx_pipeh);
494 }
495
496 /*
497 * Free command xfer buffers.
498 */
499 udl_cmdq_flush(sc);
500 udl_cmdq_free(sc);
501
502 if (sc->sc_tx_pipeh != NULL) {
503 usbd_close_pipe(sc->sc_tx_pipeh);
504 }
505
506 /*
507 * Free Huffman table.
508 */
509 udl_comp_unload(sc);
510
511 /*
512 * Free framebuffer memory.
513 */
514 udl_fbmem_free(sc);
515
516 mutex_enter(&sc->sc_thread_mtx);
517 sc->sc_dying = true;
518 cv_broadcast(&sc->sc_thread_cv);
519 mutex_exit(&sc->sc_thread_mtx);
520 kthread_join(sc->sc_thread);
521
522 cv_destroy(&sc->sc_cv);
523 mutex_destroy(&sc->sc_mtx);
524 cv_destroy(&sc->sc_thread_cv);
525 mutex_destroy(&sc->sc_thread_mtx);
526
527 /*
528 * Detach wsdisplay.
529 */
530 if (sc->sc_wsdisplay != NULL)
531 config_detach(sc->sc_wsdisplay, DETACH_FORCE);
532
533 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
534
535#ifdef UDL_EVENT_COUNTERS
536 evcnt_detach(&sc->sc_ev_cmdq_get);
537 evcnt_detach(&sc->sc_ev_cmdq_put);
538 evcnt_detach(&sc->sc_ev_cmdq_wait);
539 evcnt_detach(&sc->sc_ev_cmdq_timeout);
540#endif
541
542 return 0;
543}
544
545static int
546udl_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
547{
548 struct udl_softc *sc = v;
549#ifdef notyet
550 struct udl_ioctl_damage *d;
551#endif
552 struct wsdisplay_fbinfo *wdf;
553 u_int mode;
554
555 switch (cmd) {
556 case WSDISPLAYIO_GTYPE:
557 *(u_int *)data = WSDISPLAY_TYPE_DL;
558 return 0;
559
560 case WSDISPLAYIO_GINFO:
561 wdf = (struct wsdisplay_fbinfo *)data;
562 wdf->height = sc->sc_height;
563 wdf->width = sc->sc_width;
564 wdf->depth = sc->sc_depth;
565 wdf->cmsize = 0;
566 return 0;
567
568 case WSDISPLAYIO_GVIDEO:
569 *(u_int *)data = sc->sc_blank;
570 return 0;
571
572 case WSDISPLAYIO_SVIDEO:
573 mode = *(u_int *)data;
574 if (mode == sc->sc_blank)
575 return 0;
576 switch (mode) {
577 case WSDISPLAYIO_VIDEO_OFF:
578 udl_startstop(sc, true);
579 udl_blank(sc, 1);
580 break;
581 case WSDISPLAYIO_VIDEO_ON:
582 udl_blank(sc, 0);
583 break;
584 default:
585 return EINVAL;
586 }
587 if (UDL_CMD_BUFSIZE(sc) > 0)
588 udl_cmd_send_async(sc);
589 udl_cmdq_flush(sc);
590 sc->sc_blank = mode;
591 return 0;
592
593 case WSDISPLAYIO_SMODE:
594 mode = *(u_int *)data;
595 if (mode == sc->sc_mode)
596 return 0;
597 switch (mode) {
598 case WSDISPLAYIO_MODE_EMUL:
599 udl_startstop(sc, true);
600 /* clear screen */
601 udl_fill_rect(sc, 0, 0, 0, sc->sc_width,
602 sc->sc_height);
603 if (UDL_CMD_BUFSIZE(sc) > 0)
604 udl_cmd_send_async(sc);
605 udl_cmdq_flush(sc);
606 udl_comp_unload(sc);
607 break;
608 case WSDISPLAYIO_MODE_DUMBFB:
609 if (UDL_CMD_BUFSIZE(sc) > 0)
610 udl_cmd_send_async(sc);
611 udl_cmdq_flush(sc);
612 udl_comp_load(sc);
613 udl_startstop(sc, false);
614 break;
615 default:
616 return EINVAL;
617 }
618 sc->sc_mode = mode;
619 return 0;
620
621 case WSDISPLAYIO_LINEBYTES:
622 *(u_int *)data = sc->sc_width * (sc->sc_depth / 8);
623 return 0;
624
625#ifdef notyet
626 /*
627 * XXX
628 * OpenBSD allows device specific ioctl()s and use this
629 * UDLIO_DAMAGE for the damage extension ops of X servers.
630 * Before blindly pulling such interfaces, probably we should
631 * discuss how such devices should be handled which have
632 * in-direct framebuffer memories that should be transfered
633 * per updated rectangle regions via MI wscons APIs.
634 */
635 case UDLIO_DAMAGE:
636 d = (struct udl_ioctl_damage *)data;
637 d->status = UDLIO_STATUS_OK;
638 if (sc->sc_flags & UDL_COMPRDY)
639 udl_draw_rect_comp(sc, d);
640 else
641 udl_draw_rect(sc, d);
642 return 0;
643#endif
644 }
645
646 return EPASSTHROUGH;
647}
648
649static paddr_t
650udl_mmap(void *v, void *vs, off_t off, int prot)
651{
652 struct udl_softc *sc = v;
653 vaddr_t vaddr;
654 paddr_t paddr;
655 bool rv __diagused;
656
657 if (off < 0 || off > roundup2(UDL_FBMEM_SIZE(sc), PAGE_SIZE))
658 return -1;
659
660 /* allocate framebuffer memory */
661 if (udl_fbmem_alloc(sc) != 0)
662 return -1;
663
664 udl_startstop(sc, false);
665
666 vaddr = (vaddr_t)sc->sc_fbmem + off;
667 rv = pmap_extract(pmap_kernel(), vaddr, &paddr);
668 KASSERT(rv);
669 paddr += vaddr & PGOFSET;
670
671 /* XXX we need MI paddr_t -> mmap cookie API */
672#if defined(__alpha__)
673#define PTOMMAP(paddr) alpha_btop((char *)paddr)
674#elif defined(__arm__)
675#define PTOMMAP(paddr) arm_btop((u_long)paddr)
676#elif defined(__hppa__)
677#define PTOMMAP(paddr) btop((u_long)paddr)
678#elif defined(__i386__) || defined(__x86_64__)
679#define PTOMMAP(paddr) x86_btop(paddr)
680#elif defined(__m68k__)
681#define PTOMMAP(paddr) m68k_btop((char *)paddr)
682#elif defined(__mips__)
683#define PTOMMAP(paddr) mips_btop(paddr)
684#elif defined(__powerpc__)
685#define PTOMMAP(paddr) (paddr)
686#elif defined(__sh__)
687#define PTOMMAP(paddr) sh3_btop(paddr)
688#elif defined(__sparc__)
689#define PTOMMAP(paddr) (paddr)
690#elif defined(__sparc64__)
691#define PTOMMAP(paddr) atop(paddr)
692#elif defined(__vax__)
693#define PTOMMAP(paddr) btop((u_int)paddr)
694#endif
695
696 return PTOMMAP(paddr);
697}
698
699static int
700udl_alloc_screen(void *v, const struct wsscreen_descr *type,
701 void **cookiep, int *curxp, int *curyp, long *attrp)
702{
703 struct udl_softc *sc = v;
704
705 if (sc->sc_nscreens > 0)
706 return ENOMEM;
707
708 /*
709 * Initialize rasops.
710 */
711 sc->sc_ri.ri_depth = sc->sc_depth;
712 sc->sc_ri.ri_bits = NULL;
713 sc->sc_ri.ri_width = sc->sc_width;
714 sc->sc_ri.ri_height = sc->sc_height;
715 sc->sc_ri.ri_stride = sc->sc_width * (sc->sc_depth / 8);
716 sc->sc_ri.ri_hw = sc;
717 sc->sc_ri.ri_flg = 0;
718
719 if (sc->sc_depth == 16) {
720 sc->sc_ri.ri_rnum = 5;
721 sc->sc_ri.ri_gnum = 6;
722 sc->sc_ri.ri_bnum = 5;
723 sc->sc_ri.ri_rpos = 11;
724 sc->sc_ri.ri_gpos = 5;
725 sc->sc_ri.ri_bpos = 0;
726 }
727
728 rasops_init(&sc->sc_ri, sc->sc_height / 8, sc->sc_width / 8);
729
730 sc->sc_ri.ri_ops.cursor = udl_cursor;
731 sc->sc_ri.ri_ops.putchar = udl_putchar;
732 sc->sc_ri.ri_ops.copycols = udl_copycols;
733 sc->sc_ri.ri_ops.erasecols = udl_erasecols;
734 sc->sc_ri.ri_ops.copyrows = udl_copyrows;
735 sc->sc_ri.ri_ops.eraserows = udl_eraserows;
736
737 sc->sc_ri.ri_ops.allocattr(&sc->sc_ri, 0, 0, 0, attrp);
738
739 sc->sc_defaultscreen.ncols = sc->sc_ri.ri_cols;
740 sc->sc_defaultscreen.nrows = sc->sc_ri.ri_rows;
741 sc->sc_defaultscreen.textops = &sc->sc_ri.ri_ops;
742 sc->sc_defaultscreen.fontwidth = sc->sc_ri.ri_font->fontwidth;
743 sc->sc_defaultscreen.fontheight = sc->sc_ri.ri_font->fontheight;
744 sc->sc_defaultscreen.capabilities = sc->sc_ri.ri_caps;
745
746 *cookiep = &sc->sc_ri;
747 *curxp = 0;
748 *curyp = 0;
749
750 sc->sc_nscreens++;
751
752 return 0;
753}
754
755static void
756udl_free_screen(void *v, void *cookie)
757{
758 struct udl_softc *sc = v;
759
760 sc->sc_nscreens--;
761}
762
763static int
764udl_show_screen(void *v, void *cookie, int waitok,
765 void (*cb)(void *, int, int), void *cbarg)
766{
767
768 return 0;
769}
770
771static inline void
772udl_cmd_add_decomptable(struct udl_softc *sc, uint8_t *buf, int len)
773{
774
775 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_DECOMP);
776 udl_cmd_add_4(sc, 0x263871cd); /* magic number */
777 udl_cmd_add_4(sc, 0x00000200); /* 512 byte chunks */
778 memcpy(sc->sc_cmd_buf, buf, len);
779 sc->sc_cmd_buf += len;
780}
781
782static void
783udl_comp_load(struct udl_softc *sc)
784{
785 struct udl_huffman *h;
786 uint8_t *decomp;
787 size_t decomp_size;
788 int error, i;
789
790 if (!(sc->sc_flags & UDL_DECOMPRDY)) {
791 error = firmware_load("udl", "udl-decomp", &decomp,
792 &decomp_size);
793 if (error != 0) {
794 aprint_error_dev(sc->sc_dev,
795 "error %d, could not read decomp table %s!\n",
796 error, "udl-decomp");
797 return;
798 }
799 udl_cmd_add_decomptable(sc, decomp, decomp_size);
800 firmware_free(decomp, decomp_size);
801 if (udl_cmd_send(sc) != 0)
802 return;
803 sc->sc_flags |= UDL_DECOMPRDY;
804 }
805
806 if (!(sc->sc_flags & UDL_COMPRDY)) {
807 error = firmware_load("udl", "udl-comp", &sc->sc_huffman,
808 &sc->sc_huffman_size);
809 if (error != 0) {
810 aprint_error_dev(sc->sc_dev,
811 "error %d, could not read huffman table %s!\n",
812 error, "udl-comp");
813 return;
814 }
815 h = (struct udl_huffman *)sc->sc_huffman;
816 for (i = 0; i < UDL_HUFFMAN_RECORDS; i++)
817 h[i].bit_pattern = be32toh(h[i].bit_pattern);
818 sc->sc_huffman_base = sc->sc_huffman + UDL_HUFFMAN_BASE;
819 sc->sc_flags |= UDL_COMPRDY;
820 }
821}
822
823static void
824udl_comp_unload(struct udl_softc *sc)
825{
826
827 if (sc->sc_flags & UDL_COMPRDY) {
828 firmware_free(sc->sc_huffman, sc->sc_huffman_size);
829 sc->sc_huffman = NULL;
830 sc->sc_huffman_size = 0;
831 sc->sc_flags &= ~UDL_COMPRDY;
832 }
833}
834
835static int
836udl_fbmem_alloc(struct udl_softc *sc)
837{
838
839 mutex_enter(&sc->sc_thread_mtx);
840 if (sc->sc_fbmem == NULL) {
841 sc->sc_fbmem = kmem_zalloc(UDL_FBMEM_SIZE(sc), KM_SLEEP);
842 if (sc->sc_fbmem == NULL) {
843 mutex_exit(&sc->sc_thread_mtx);
844 return -1;
845 }
846 }
847 if (sc->sc_fbmem_prev == NULL) {
848 sc->sc_fbmem_prev = kmem_zalloc(UDL_FBMEM_SIZE(sc), KM_SLEEP);
849 if (sc->sc_fbmem_prev == NULL) {
850 kmem_free(sc->sc_fbmem, UDL_FBMEM_SIZE(sc));
851 sc->sc_fbmem = NULL;
852 mutex_exit(&sc->sc_thread_mtx);
853 return -1;
854 }
855 }
856 mutex_exit(&sc->sc_thread_mtx);
857
858 return 0;
859}
860
861static void
862udl_fbmem_free(struct udl_softc *sc)
863{
864
865 mutex_enter(&sc->sc_thread_mtx);
866 if (sc->sc_fbmem != NULL) {
867 kmem_free(sc->sc_fbmem, UDL_FBMEM_SIZE(sc));
868 sc->sc_fbmem = NULL;
869 }
870 if (sc->sc_fbmem_prev != NULL) {
871 kmem_free(sc->sc_fbmem_prev, UDL_FBMEM_SIZE(sc));
872 sc->sc_fbmem_prev = NULL;
873 }
874 mutex_exit(&sc->sc_thread_mtx);
875}
876
877static int
878udl_cmdq_alloc(struct udl_softc *sc)
879{
880 struct udl_cmdq *cmdq;
881 int i;
882
883 TAILQ_INIT(&sc->sc_freecmd);
884 TAILQ_INIT(&sc->sc_xfercmd);
885
886 for (i = 0; i < UDL_NCMDQ; i++) {
887 cmdq = &sc->sc_cmdq[i];
888
889 cmdq->cq_sc = sc;
890
891 int err = usbd_create_xfer(sc->sc_tx_pipeh,
892 UDL_CMD_BUFFER_SIZE, 0, 0, &cmdq->cq_xfer);
893 if (err) {
894 aprint_error_dev(sc->sc_dev,
895 "%s: can't allocate xfer handle!\n", __func__);
896 goto error;
897 }
898
899 cmdq->cq_buf = usbd_get_buffer(cmdq->cq_xfer);
900
901 TAILQ_INSERT_TAIL(&sc->sc_freecmd, cmdq, cq_chain);
902 }
903
904 return 0;
905
906 error:
907 udl_cmdq_free(sc);
908 return -1;
909}
910
911static void
912udl_cmdq_free(struct udl_softc *sc)
913{
914 struct udl_cmdq *cmdq;
915 int i;
916
917 for (i = 0; i < UDL_NCMDQ; i++) {
918 cmdq = &sc->sc_cmdq[i];
919
920 if (cmdq->cq_xfer != NULL) {
921 usbd_destroy_xfer(cmdq->cq_xfer);
922 cmdq->cq_xfer = NULL;
923 cmdq->cq_buf = NULL;
924 }
925 }
926}
927
928static struct udl_cmdq *
929udl_cmdq_get(struct udl_softc *sc)
930{
931 struct udl_cmdq *cmdq;
932
933 cmdq = TAILQ_FIRST(&sc->sc_freecmd);
934 if (cmdq != NULL) {
935 TAILQ_REMOVE(&sc->sc_freecmd, cmdq, cq_chain);
936 UDL_EVCNT_INCR(&sc->sc_ev_cmdq_get);
937 }
938
939 return cmdq;
940}
941
942static void
943udl_cmdq_put(struct udl_softc *sc, struct udl_cmdq *cmdq)
944{
945
946 TAILQ_INSERT_TAIL(&sc->sc_freecmd, cmdq, cq_chain);
947 UDL_EVCNT_INCR(&sc->sc_ev_cmdq_put);
948}
949
950static void
951udl_cmdq_flush(struct udl_softc *sc)
952{
953
954 mutex_enter(&sc->sc_mtx);
955 while (TAILQ_FIRST(&sc->sc_xfercmd) != NULL)
956 cv_wait(&sc->sc_cv, &sc->sc_mtx);
957 mutex_exit(&sc->sc_mtx);
958}
959
960static void
961udl_cursor(void *cookie, int on, int row, int col)
962{
963 struct rasops_info *ri = cookie;
964 struct udl_softc *sc = ri->ri_hw;
965 int x, y, width, height;
966
967 if (ri->ri_flg & RI_CURSOR)
968 udl_restore_char(ri);
969
970 ri->ri_crow = row;
971 ri->ri_ccol = col;
972
973 if (on != 0) {
974 ri->ri_flg |= RI_CURSOR;
975
976 x = col * ri->ri_font->fontwidth;
977 y = row * ri->ri_font->fontheight;
978 width = ri->ri_font->fontwidth;
979 height = ri->ri_font->fontheight;
980
981 /* save the last character block to off-screen */
982 udl_copy_rect(sc, x, y, 0, sc->sc_offscreen, width, height);
983
984 /* draw cursor */
985 udl_fill_rect(sc, 0xffff, x, y, width, 1);
986 udl_fill_rect(sc, 0xffff, x, y + 1, 1, height - 2);
987 udl_fill_rect(sc, 0xffff, x + width - 1, y + 1, 1, height - 2);
988 udl_fill_rect(sc, 0xffff, x, y + height - 1, width, 1);
989
990 udl_cmd_send_async(sc);
991 } else
992 ri->ri_flg &= ~RI_CURSOR;
993}
994
995static void
996udl_putchar(void *cookie, int row, int col, u_int uc, long attr)
997{
998 struct rasops_info *ri = cookie;
999 struct udl_softc *sc = ri->ri_hw;
1000 uint16_t rgb16[2];
1001 int fg, bg, underline, x, y, width, height;
1002
1003 rasops_unpack_attr(attr, &fg, &bg, &underline);
1004 rgb16[1] = (uint16_t)ri->ri_devcmap[fg];
1005 rgb16[0] = (uint16_t)ri->ri_devcmap[bg];
1006
1007 x = col * ri->ri_font->fontwidth;
1008 y = row * ri->ri_font->fontheight;
1009 width = ri->ri_font->fontwidth;
1010 height = ri->ri_font->fontheight;
1011
1012 if (uc == ' ') {
1013 /*
1014 * Writting a block for the space character instead rendering
1015 * it from font bits is more slim.
1016 */
1017 udl_fill_rect(sc, rgb16[0], x, y, width, height);
1018 } else {
1019 /* render a character from font bits */
1020 udl_draw_char(ri, rgb16, uc, x, y);
1021 }
1022
1023 if (underline != 0)
1024 udl_fill_rect(sc, rgb16[1], x, y + height - 1, width, 1);
1025
1026#if 0
1027 udl_cmd_send_async(sc);
1028#endif
1029}
1030
1031static void
1032udl_copycols(void *cookie, int row, int src, int dst, int num)
1033{
1034 struct rasops_info *ri = cookie;
1035 struct udl_softc *sc = ri->ri_hw;
1036 int sx, dx, y, width, height;
1037
1038 sx = src * ri->ri_font->fontwidth;
1039 dx = dst * ri->ri_font->fontwidth;
1040 y = row * ri->ri_font->fontheight;
1041 width = num * ri->ri_font->fontwidth;
1042 height = ri->ri_font->fontheight;
1043
1044 /* copy row block to off-screen first to fix overlay-copy problem */
1045 udl_copy_rect(sc, sx, y, 0, sc->sc_offscreen, width, height);
1046
1047 /* copy row block back from off-screen now */
1048 udl_copy_rect(sc, 0, sc->sc_offscreen, dx, y, width, height);
1049#if 0
1050 udl_cmd_send_async(sc);
1051#endif
1052}
1053
1054static void
1055udl_erasecols(void *cookie, int row, int col, int num, long attr)
1056{
1057 struct rasops_info *ri = cookie;
1058 struct udl_softc *sc = ri->ri_hw;
1059 uint16_t rgb16;
1060 int fg, bg, x, y, width, height;
1061
1062 rasops_unpack_attr(attr, &fg, &bg, NULL);
1063 rgb16 = (uint16_t)ri->ri_devcmap[bg];
1064
1065 x = col * ri->ri_font->fontwidth;
1066 y = row * ri->ri_font->fontheight;
1067 width = num * ri->ri_font->fontwidth;
1068 height = ri->ri_font->fontheight;
1069
1070 udl_fill_rect(sc, rgb16, x, y, width, height);
1071#if 0
1072 udl_cmd_send_async(sc);
1073#endif
1074}
1075
1076static void
1077udl_copyrows(void *cookie, int src, int dst, int num)
1078{
1079 struct rasops_info *ri = cookie;
1080 struct udl_softc *sc = ri->ri_hw;
1081 int sy, ey, dy, width, height;
1082
1083 width = ri->ri_emuwidth;
1084 height = ri->ri_font->fontheight;
1085
1086 if (dst < src) {
1087 sy = src * height;
1088 ey = (src + num) * height;
1089 dy = dst * height;
1090
1091 while (sy < ey) {
1092 udl_copy_rect(sc, 0, sy, 0, dy, width, height);
1093 sy += height;
1094 dy += height;
1095 }
1096 } else {
1097 sy = (src + num) * height;
1098 ey = src * height;
1099 dy = (dst + num) * height;
1100
1101 while (sy > ey) {
1102 sy -= height;
1103 dy -= height;
1104 udl_copy_rect(sc, 0, sy, 0, dy, width, height);
1105 }
1106 }
1107#if 0
1108 udl_cmd_send_async(sc);
1109#endif
1110}
1111
1112static void
1113udl_eraserows(void *cookie, int row, int num, long attr)
1114{
1115 struct rasops_info *ri = cookie;
1116 struct udl_softc *sc = ri->ri_hw;
1117 uint16_t rgb16;
1118 int fg, bg, y, width, height;
1119
1120 rasops_unpack_attr(attr, &fg, &bg, NULL);
1121 rgb16 = (uint16_t)ri->ri_devcmap[bg];
1122
1123 y = row * ri->ri_font->fontheight;
1124 width = ri->ri_emuwidth;
1125 height = num * ri->ri_font->fontheight;
1126
1127 udl_fill_rect(sc, rgb16, 0, y, width, height);
1128#if 0
1129 udl_cmd_send_async(sc);
1130#endif
1131}
1132
1133static void
1134udl_restore_char(struct rasops_info *ri)
1135{
1136 struct udl_softc *sc = ri->ri_hw;
1137 int x, y, width, height;
1138
1139 x = ri->ri_ccol * ri->ri_font->fontwidth;
1140 y = ri->ri_crow * ri->ri_font->fontheight;
1141 width = ri->ri_font->fontwidth;
1142 height = ri->ri_font->fontheight;
1143
1144 /* restore the last saved character from off-screen */
1145 udl_copy_rect(sc, 0, sc->sc_offscreen, x, y, width, height);
1146}
1147
1148static void
1149udl_draw_char(struct rasops_info *ri, uint16_t *rgb16, u_int uc, int x, int y)
1150{
1151 struct udl_softc *sc = ri->ri_hw;
1152 struct wsdisplay_font *font = ri->ri_font;
1153 uint32_t fontbits;
1154 uint16_t pixels[32];
1155 uint8_t *fontbase;
1156 int i, soff, eoff;
1157
1158 soff = y * sc->sc_width + x;
1159 eoff = (y + font->fontheight) * sc->sc_width + x;
1160 fontbase = (uint8_t *)font->data + (uc - font->firstchar) *
1161 ri->ri_fontscale;
1162
1163 while (soff < eoff) {
1164 fontbits = 0;
1165 switch (font->stride) {
1166 case 4:
1167 fontbits |= fontbase[3];
1168 /* FALLTHROUGH */
1169 case 3:
1170 fontbits |= fontbase[2] << 8;
1171 /* FALLTHROUGH */
1172 case 2:
1173 fontbits |= fontbase[1] << 16;
1174 /* FALLTHROUGH */
1175 case 1:
1176 fontbits |= fontbase[0] << 24;
1177 }
1178 fontbase += font->stride;
1179
1180 for (i = 0; i < font->fontwidth; i++) {
1181 pixels[i] = rgb16[(fontbits >> 31) & 1];
1182 fontbits <<= 1;
1183 }
1184
1185 udl_draw_line(sc, pixels, soff, font->fontwidth);
1186 soff += sc->sc_width;
1187 }
1188}
1189
1190static void
1191udl_copy_rect(struct udl_softc *sc, int sx, int sy, int dx, int dy, int width,
1192 int height)
1193{
1194 int sbase, soff, ebase, eoff, dbase, doff, width_cur;
1195
1196 sbase = sy * sc->sc_width;
1197 ebase = (sy + height) * sc->sc_width;
1198 dbase = dy * sc->sc_width;
1199
1200 while (width > 0) {
1201 soff = sbase + sx;
1202 eoff = ebase + sx;
1203 doff = dbase + dx;
1204
1205 if (width >= UDL_CMD_WIDTH_MAX)
1206 width_cur = UDL_CMD_WIDTH_MAX;
1207 else
1208 width_cur = width;
1209
1210 while (soff < eoff) {
1211 udl_copy_line(sc, soff, doff, width_cur);
1212 soff += sc->sc_width;
1213 doff += sc->sc_width;
1214 }
1215
1216 sx += width_cur;
1217 dx += width_cur;
1218 width -= width_cur;
1219 }
1220}
1221
1222static void
1223udl_fill_rect(struct udl_softc *sc, uint16_t rgb16, int x, int y, int width,
1224 int height)
1225{
1226 int sbase, soff, ebase, eoff, width_cur;
1227
1228 sbase = y * sc->sc_width;
1229 ebase = (y + height) * sc->sc_width;
1230
1231 while (width > 0) {
1232 soff = sbase + x;
1233 eoff = ebase + x;
1234
1235 if (width >= UDL_CMD_WIDTH_MAX)
1236 width_cur = UDL_CMD_WIDTH_MAX;
1237 else
1238 width_cur = width;
1239
1240 while (soff < eoff) {
1241 udl_fill_line(sc, rgb16, soff, width_cur);
1242 soff += sc->sc_width;
1243 }
1244
1245 x += width_cur;
1246 width -= width_cur;
1247 }
1248}
1249
1250#ifdef notyet
1251static void
1252udl_draw_rect(struct udl_softc *sc, struct udl_ioctl_damage *d)
1253{
1254 int sbase, soff, ebase, eoff, x, y, width, width_cur, height;
1255
1256 x = d->x1;
1257 y = d->y1;
1258 width = d->x2 - d->x1;
1259 height = d->y2 - d->y1;
1260 sbase = y * sc->sc_width;
1261 ebase = (y + height) * sc->sc_width;
1262
1263 while (width > 0) {
1264 soff = sbase + x;
1265 eoff = ebase + x;
1266
1267 if (width >= UDL_CMD_WIDTH_MAX)
1268 width_cur = UDL_CMD_WIDTH_MAX;
1269 else
1270 width_cur = width;
1271
1272 while (soff < eoff) {
1273 udl_draw_line(sc, (uint16_t *)sc->sc_fbmem + soff,
1274 soff, width_cur);
1275 soff += sc->sc_width;
1276 }
1277
1278 x += width_cur;
1279 width -= width_cur;
1280 }
1281
1282 udl_cmd_send_async(sc);
1283}
1284
1285static void
1286udl_draw_rect_comp(struct udl_softc *sc, struct udl_ioctl_damage *d)
1287{
1288 int soff, eoff, x, y, width, height;
1289
1290 x = d->x1;
1291 y = d->y1;
1292 width = d->x2 - d->x1;
1293 height = d->y2 - d->y1;
1294 soff = y * sc->sc_width + x;
1295 eoff = (y + height) * sc->sc_width + x;
1296
1297 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff);
1298 sc->sc_cmd_cblen = 4;
1299
1300 while (soff < eoff) {
1301 udl_draw_line_comp(sc, (uint16_t *)sc->sc_fbmem + soff, soff,
1302 width);
1303 soff += sc->sc_width;
1304 }
1305
1306 udl_cmd_send_async(sc);
1307}
1308#endif
1309
1310static inline void
1311udl_copy_line(struct udl_softc *sc, int soff, int doff, int width)
1312{
1313
1314 if (__predict_false((UDL_CMD_BUFSIZE(sc) + UDL_CMD_COPY_SIZE + 2) >
1315 UDL_CMD_BUFFER_SIZE))
1316 udl_cmd_send_async(sc);
1317
1318 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_COPY16);
1319 udl_cmd_add_4(sc, ((doff * 2) << 8) | (width & 0xff));
1320
1321 udl_cmd_add_3(sc, soff * 2);
1322}
1323
1324static inline void
1325udl_fill_line(struct udl_softc *sc, uint16_t rgb16, int off, int width)
1326{
1327
1328 if (__predict_false((UDL_CMD_BUFSIZE(sc) + UDL_CMD_FILL_SIZE + 2) >
1329 UDL_CMD_BUFFER_SIZE))
1330 udl_cmd_send_async(sc);
1331
1332 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_RLE16);
1333 udl_cmd_add_4(sc, ((off * 2) << 8) | (width & 0xff));
1334
1335 udl_cmd_add_1(sc, width);
1336 udl_cmd_add_2(sc, rgb16);
1337}
1338
1339static inline void
1340udl_draw_line(struct udl_softc *sc, uint16_t *buf, int off, int width)
1341{
1342
1343 if (__predict_false(
1344 (UDL_CMD_BUFSIZE(sc) + UDL_CMD_DRAW_SIZE(width) + 2) >
1345 UDL_CMD_BUFFER_SIZE))
1346 udl_cmd_send_async(sc);
1347
1348 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_FB_WRITE16);
1349 udl_cmd_add_4(sc, ((off * 2) << 8) | (width & 0xff));
1350
1351 udl_cmd_add_buf(sc, buf, width);
1352}
1353
1354#ifdef notyet
1355static inline int
1356udl_cmd_add_buf_comp(struct udl_softc *sc, uint16_t *buf, int width)
1357{
1358 struct udl_huffman *h;
1359 uint16_t *startp, *endp;
1360 uint32_t bit_pattern;
1361 uint16_t prev;
1362 int16_t diff;
1363 uint8_t bit_count, bit_pos, bit_rem, curlen;
1364
1365 startp = buf;
1366 if (width >= UDL_CMD_WIDTH_MAX)
1367 endp = buf + UDL_CMD_WIDTH_MAX;
1368 else
1369 endp = buf + width;
1370
1371 prev = bit_pos = *sc->sc_cmd_buf = 0;
1372 bit_rem = 8;
1373
1374 /*
1375 * Generate a sub-block with maximal 256 pixels compressed data.
1376 */
1377 while (buf < endp) {
1378 /* get difference between current and previous pixel */
1379 diff = *buf - prev;
1380
1381 /* get the huffman difference bit sequence */
1382 h = (struct udl_huffman *)sc->sc_huffman_base + diff;
1383 bit_count = h->bit_count;
1384 bit_pattern = h->bit_pattern;
1385
1386 curlen = (bit_pos + bit_count + 7) / 8;
1387 if (__predict_false((sc->sc_cmd_cblen + curlen + 1) >
1388 UDL_CMD_COMP_BLOCK_SIZE))
1389 break;
1390
1391 /* generate one pixel compressed data */
1392 while (bit_count >= bit_rem) {
1393 *sc->sc_cmd_buf++ |=
1394 (bit_pattern & ((1 << bit_rem) - 1)) << bit_pos;
1395 *sc->sc_cmd_buf = 0;
1396 sc->sc_cmd_cblen++;
1397 bit_count -= bit_rem;
1398 bit_pattern >>= bit_rem;
1399 bit_pos = 0;
1400 bit_rem = 8;
1401 }
1402
1403 if (bit_count > 0) {
1404 *sc->sc_cmd_buf |=
1405 (bit_pattern & ((1 << bit_count) - 1)) << bit_pos;
1406 bit_pos += bit_count;
1407 bit_rem -= bit_count;
1408 }
1409
1410 prev = *buf++;
1411 }
1412
1413 /*
1414 * If we have bits left in our last byte, round up to the next
1415 * byte, so we don't overwrite them.
1416 */
1417 if (bit_pos > 0) {
1418 sc->sc_cmd_buf++;
1419 sc->sc_cmd_cblen++;
1420 }
1421
1422 /* return how many pixels we have compressed */
1423 return buf - startp;
1424}
1425
1426static inline void
1427udl_draw_line_comp(struct udl_softc *sc, uint16_t *buf, int off, int width)
1428{
1429 uint8_t *widthp;
1430 int width_cur;
1431
1432 while (width > 0) {
1433 if (__predict_false(
1434 (sc->sc_cmd_cblen + UDL_CMD_COMP_MIN_SIZE + 1) >
1435 UDL_CMD_COMP_BLOCK_SIZE)) {
1436 if (UDL_CMD_BUFSIZE(sc) < UDL_CMD_COMP_THRESHOLD) {
1437 while (sc->sc_cmd_cblen <
1438 UDL_CMD_COMP_BLOCK_SIZE) {
1439 *sc->sc_cmd_buf++ = 0;
1440 sc->sc_cmd_cblen++;
1441 }
1442 } else
1443 udl_cmd_send_async(sc);
1444 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff);
1445 sc->sc_cmd_cblen = 4;
1446 }
1447
1448 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) |
1449 (UDL_BULK_CMD_FB_WRITE16 | UDL_BULK_CMD_FB_COMP));
1450 udl_cmd_add_4(sc, (off * 2) << 8);
1451
1452 widthp = sc->sc_cmd_buf - 1;
1453
1454 sc->sc_cmd_cblen += UDL_CMD_HEADER_SIZE;
1455
1456 width_cur = udl_cmd_add_buf_comp(sc, buf, width);
1457
1458 *widthp = width_cur;
1459 buf += width_cur;
1460 off += width_cur;
1461 width -= width_cur;
1462 }
1463}
1464#endif
1465
1466static int
1467udl_cmd_send(struct udl_softc *sc)
1468{
1469 struct udl_cmdq *cmdq;
1470 usbd_status error;
1471 uint32_t len;
1472
1473 cmdq = sc->sc_cmd_cur;
1474
1475 /* mark end of command stack */
1476 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_EOC);
1477
1478 len = UDL_CMD_BUFSIZE(sc);
1479
1480 /* do xfer */
1481 error = usbd_bulk_transfer(cmdq->cq_xfer, sc->sc_tx_pipeh, 0,
1482 USBD_NO_TIMEOUT, cmdq->cq_buf, &len);
1483
1484 UDL_CMD_BUFINIT(sc);
1485
1486 if (error != USBD_NORMAL_COMPLETION) {
1487 aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__,
1488 usbd_errstr(error));
1489 return -1;
1490 }
1491
1492 return 0;
1493}
1494
1495static void
1496udl_cmd_send_async(struct udl_softc *sc)
1497{
1498 struct udl_cmdq *cmdq;
1499 usbd_status error;
1500 uint32_t len;
1501
1502#if 1
1503 /*
1504 * XXX
1505 * All tty ops for wsemul are called with tty_lock spin mutex held,
1506 * so we can't call cv_wait(9) here to acquire a free buffer.
1507 * For now, all commands and data for wsemul ops are discarded
1508 * if there is no free command buffer, and then screen text might
1509 * be corrupted on large scroll ops etc.
1510 *
1511 * Probably we have to reorganize the giant tty_lock mutex, or
1512 * change wsdisplay APIs (especially wsdisplaystart()) to return
1513 * a number of actually handled characters as OpenBSD does, but
1514 * the latter one requires whole API changes around rasops(9) etc.
1515 */
1516 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
1517 if (TAILQ_FIRST(&sc->sc_freecmd) == NULL) {
1518 UDL_CMD_BUFINIT(sc);
1519 return;
1520 }
1521 }
1522#endif
1523
1524 cmdq = sc->sc_cmd_cur;
1525
1526 /* mark end of command stack */
1527 udl_cmd_add_2(sc, (UDL_BULK_SOC << 8) | UDL_BULK_CMD_EOC);
1528
1529 len = UDL_CMD_BUFSIZE(sc);
1530
1531 /* do xfer */
1532 mutex_enter(&sc->sc_mtx);
1533 usbd_setup_xfer(cmdq->cq_xfer, cmdq, cmdq->cq_buf,
1534 len, 0, USBD_NO_TIMEOUT, udl_cmd_send_async_cb);
1535 error = usbd_transfer(cmdq->cq_xfer);
1536 if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
1537 aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__,
1538 usbd_errstr(error));
1539 mutex_exit(&sc->sc_mtx);
1540 goto end;
1541 }
1542
1543 TAILQ_INSERT_TAIL(&sc->sc_xfercmd, cmdq, cq_chain);
1544 cmdq = udl_cmdq_get(sc);
1545 mutex_exit(&sc->sc_mtx);
1546 while (cmdq == NULL) {
1547 int err;
1548 UDL_EVCNT_INCR(&sc->sc_ev_cmdq_wait);
1549 mutex_enter(&sc->sc_mtx);
1550 err = cv_timedwait(&sc->sc_cv, &sc->sc_mtx,
1551 mstohz(100) /* XXX is this needed? */);
1552 if (err != 0) {
1553 DPRINTF(("%s: %s: cv timeout (error = %d)\n",
1554 device_xname(sc->sc_dev), __func__, err));
1555 UDL_EVCNT_INCR(&sc->sc_ev_cmdq_timeout);
1556 }
1557 cmdq = udl_cmdq_get(sc);
1558 mutex_exit(&sc->sc_mtx);
1559 }
1560 sc->sc_cmd_cur = cmdq;
1561 end:
1562 UDL_CMD_BUFINIT(sc);
1563}
1564
1565static void
1566udl_cmd_send_async_cb(struct usbd_xfer *xfer, void * priv,
1567 usbd_status status)
1568{
1569 struct udl_cmdq *cmdq = priv;
1570 struct udl_softc *sc = cmdq->cq_sc;
1571
1572 if (status != USBD_NORMAL_COMPLETION) {
1573 aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__,
1574 usbd_errstr(status));
1575
1576 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
1577 return;
1578 if (status == USBD_STALLED)
1579 usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh);
1580 }
1581
1582 mutex_enter(&sc->sc_mtx);
1583 TAILQ_REMOVE(&sc->sc_xfercmd, cmdq, cq_chain);
1584 udl_cmdq_put(sc, cmdq);
1585
1586 /* signal xfer op that sleeps for a free xfer buffer */
1587 cv_signal(&sc->sc_cv);
1588 mutex_exit(&sc->sc_mtx);
1589}
1590
1591static int
1592udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r, uint16_t index,
1593 uint16_t value, uint8_t *buf, uint16_t len)
1594{
1595 usb_device_request_t req;
1596 usbd_status error;
1597
1598 req.bmRequestType = rt;
1599 req.bRequest = r;
1600 USETW(req.wIndex, index);
1601 USETW(req.wValue, value);
1602 USETW(req.wLength, len);
1603
1604 error = usbd_do_request(sc->sc_udev, &req, buf);
1605 if (error != USBD_NORMAL_COMPLETION) {
1606 aprint_error_dev(sc->sc_dev, "%s: %s!\n", __func__,
1607 usbd_errstr(error));
1608 return -1;
1609 }
1610
1611 return 0;
1612}
1613
1614static int
1615udl_init(struct udl_softc *sc)
1616{
1617 static uint8_t key[16] = {
1618 0x57, 0xcd, 0xdc, 0xa7, 0x1c, 0x88, 0x5e, 0x15,
1619 0x60, 0xfe, 0xc6, 0x97, 0x16, 0x3d, 0x47, 0xf2
1620 };
1621 uint8_t status[4], val;
1622
1623 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_STATUS,
1624 0x0000, 0x0000, status, sizeof(status)) != 0)
1625 return -1;
1626
1627 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_1,
1628 0xc484, 0x0000, &val, 1) != 0)
1629 return -1;
1630
1631 val = 1;
1632 if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_WRITE_1,
1633 0xc41f, 0x0000, &val, 1) != 0)
1634 return -1;
1635
1636 if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_SET_KEY,
1637 0x0000, 0x0000, key, sizeof(key)) != 0)
1638 return -1;
1639
1640 val = 0;
1641 if (udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE, UDL_CTRL_CMD_WRITE_1,
1642 0xc40b, 0x0000, &val, 1) != 0)
1643 return -1;
1644
1645 return 0;
1646}
1647
1648static void
1649udl_read_edid(struct udl_softc *sc)
1650{
1651 uint8_t buf[64], edid[128];
1652 int offset;
1653
1654 memset(&sc->sc_ei, 0, sizeof(struct edid_info));
1655
1656 offset = 0;
1657 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID,
1658 0x00a1, (offset << 8), buf, 64) != 0)
1659 return;
1660 if (buf[0] != 0)
1661 return;
1662 memcpy(&edid[offset], &buf[1], 63);
1663 offset += 63;
1664
1665 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID,
1666 0x00a1, (offset << 8), buf, 64) != 0)
1667 return;
1668 if (buf[0] != 0)
1669 return;
1670 memcpy(&edid[offset], &buf[1], 63);
1671 offset += 63;
1672
1673 if (udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE, UDL_CTRL_CMD_READ_EDID,
1674 0x00a1, (offset << 8), buf, 3) != 0)
1675 return;
1676 if (buf[0] != 0)
1677 return;
1678 memcpy(&edid[offset], &buf[1], 2);
1679
1680 if (edid_parse(edid, &sc->sc_ei) == 0) {
1681#ifdef UDL_DEBUG
1682 edid_print(&sc->sc_ei);
1683#endif
1684 }
1685}
1686
1687static void
1688udl_set_address(struct udl_softc *sc, int start16, int stride16, int start8,
1689 int stride8)
1690{
1691 udl_reg_write_1(sc, UDL_REG_SYNC, 0x00);
1692 udl_reg_write_3(sc, UDL_REG_ADDR_START16, start16);
1693 udl_reg_write_3(sc, UDL_REG_ADDR_STRIDE16, stride16);
1694 udl_reg_write_3(sc, UDL_REG_ADDR_START8, start8);
1695 udl_reg_write_3(sc, UDL_REG_ADDR_STRIDE8, stride8);
1696 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff);
1697}
1698
1699static void
1700udl_blank(struct udl_softc *sc, int blank)
1701{
1702
1703 if (blank != 0)
1704 udl_reg_write_1(sc, UDL_REG_BLANK, UDL_REG_BLANK_ON);
1705 else
1706 udl_reg_write_1(sc, UDL_REG_BLANK, UDL_REG_BLANK_OFF);
1707 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff);
1708}
1709
1710static uint16_t
1711udl_lfsr(uint16_t count)
1712{
1713 uint16_t val = 0xffff;
1714
1715 while (count > 0) {
1716 val = (uint16_t)(val << 1) | ((uint16_t)(
1717 (uint16_t)(val << 0) ^
1718 (uint16_t)(val << 11) ^
1719 (uint16_t)(val << 13) ^
1720 (uint16_t)(val << 14)
1721 ) >> 15);
1722 count--;
1723 }
1724
1725 return val;
1726}
1727
1728static int
1729udl_set_resolution(struct udl_softc *sc, const struct videomode *vmp)
1730{
1731 uint16_t val;
1732 int start16, stride16, start8, stride8;
1733
1734 /* set video memory offsets */
1735 start16 = 0;
1736 stride16 = sc->sc_width * 2;
1737 start8 = stride16 * sc->sc_height;
1738 stride8 = sc->sc_width;
1739 udl_set_address(sc, start16, stride16, start8, stride8);
1740
1741 /* write resolution values */
1742 udl_reg_write_1(sc, UDL_REG_SYNC, 0x00);
1743 udl_reg_write_1(sc, UDL_REG_COLORDEPTH, UDL_REG_COLORDEPTH_16);
1744 val = vmp->htotal - vmp->hsync_start;
1745 udl_reg_write_2(sc, UDL_REG_XDISPLAYSTART, udl_lfsr(val));
1746 val += vmp->hdisplay;
1747 udl_reg_write_2(sc, UDL_REG_XDISPLAYEND, udl_lfsr(val));
1748 val = vmp->vtotal - vmp->vsync_start;
1749 udl_reg_write_2(sc, UDL_REG_YDISPLAYSTART, udl_lfsr(val));
1750 val += vmp->vdisplay;
1751 udl_reg_write_2(sc, UDL_REG_YDISPLAYEND, udl_lfsr(val));
1752 val = vmp->htotal - 1;
1753 udl_reg_write_2(sc, UDL_REG_XENDCOUNT, udl_lfsr(val));
1754 val = vmp->hsync_end - vmp->hsync_start + 1;
1755 if (vmp->flags & VID_PHSYNC) {
1756 udl_reg_write_2(sc, UDL_REG_HSYNCSTART, udl_lfsr(1));
1757 udl_reg_write_2(sc, UDL_REG_HSYNCEND, udl_lfsr(val));
1758 } else {
1759 udl_reg_write_2(sc, UDL_REG_HSYNCSTART, udl_lfsr(val));
1760 udl_reg_write_2(sc, UDL_REG_HSYNCEND, udl_lfsr(1));
1761 }
1762 val = vmp->hdisplay;
1763 udl_reg_write_2(sc, UDL_REG_HPIXELS, val);
1764 val = vmp->vtotal;
1765 udl_reg_write_2(sc, UDL_REG_YENDCOUNT, udl_lfsr(val));
1766 val = vmp->vsync_end - vmp->vsync_start;
1767 if (vmp->flags & VID_PVSYNC) {
1768 udl_reg_write_2(sc, UDL_REG_VSYNCSTART, udl_lfsr(0));
1769 udl_reg_write_2(sc, UDL_REG_VSYNCEND, udl_lfsr(val));
1770 } else {
1771 udl_reg_write_2(sc, UDL_REG_VSYNCSTART, udl_lfsr(val));
1772 udl_reg_write_2(sc, UDL_REG_VSYNCEND, udl_lfsr(0));
1773 }
1774 val = vmp->vdisplay;
1775 udl_reg_write_2(sc, UDL_REG_VPIXELS, val);
1776 val = vmp->dot_clock / 5;
1777 udl_reg_write_2(sc, UDL_REG_PIXELCLOCK5KHZ, bswap16(val));
1778 udl_reg_write_1(sc, UDL_REG_SYNC, 0xff);
1779
1780 if (udl_cmd_send(sc) != 0)
1781 return -1;
1782
1783 /* clear screen */
1784 udl_fill_rect(sc, 0, 0, 0, sc->sc_width, sc->sc_height);
1785
1786 if (udl_cmd_send(sc) != 0)
1787 return -1;
1788
1789 /* show framebuffer content */
1790 udl_blank(sc, 0);
1791
1792 if (udl_cmd_send(sc) != 0)
1793 return -1;
1794
1795 sc->sc_blank = WSDISPLAYIO_VIDEO_ON;
1796
1797 return 0;
1798}
1799
1800static const struct videomode *
1801udl_videomode_lookup(const char *name)
1802{
1803 int i;
1804
1805 for (i = 0; i < videomode_count; i++)
1806 if (strcmp(name, videomode_list[i].name) == 0)
1807 return &videomode_list[i];
1808
1809 return NULL;
1810}
1811
1812static void
1813udl_update_thread(void *v)
1814{
1815 struct udl_softc *sc = v;
1816 int stride;
1817#ifdef notyet
1818 bool update = false;
1819 int linecount, x, y;
1820 uint16_t *fb, *fbcopy;
1821 uint8_t *curfb;
1822#else
1823 uint16_t *fb;
1824 int offs;
1825#endif
1826
1827 mutex_enter(&sc->sc_thread_mtx);
1828
1829 for (;;) {
1830 stride = min(sc->sc_width, UDL_CMD_WIDTH_MAX - 8);
1831 if (sc->sc_dying == true) {
1832 mutex_exit(&sc->sc_thread_mtx);
1833 kthread_exit(0);
1834 }
1835
1836 if (sc->sc_thread_stop == true || sc->sc_fbmem == NULL)
1837 goto thread_wait;
1838
1839#ifdef notyet
1840 curfb = kmem_zalloc(UDL_FBMEM_SIZE(sc), KM_SLEEP);
1841 memcpy(curfb, sc->sc_fbmem, sc->sc_height * sc->sc_width * 2);
1842 fb = (uint16_t *)curfb;
1843 fbcopy = (uint16_t *)sc->sc_fbmem_prev;
1844 for (y = 0; y < sc->sc_height; y++) {
1845 linecount = 0;
1846 update = false;
1847 for (x = 0; x < sc->sc_width; x++) {
1848 if (linecount >= stride) {
1849 udl_draw_line(sc, &fb[y * sc->sc_width
1850 + x - linecount], y * sc->sc_width
1851 + x - linecount, linecount);
1852 linecount = 0;
1853 update = false;
1854 }
1855 if (fb[y * sc->sc_width + x] ^ fbcopy[y *
1856 sc->sc_width + x]) {
1857 update = true;
1858 linecount ++;
1859 } else if (update == true) {
1860 udl_draw_line(sc, &fb[y * sc->sc_width
1861 + x - linecount], y * sc->sc_width
1862 + x - linecount, linecount);
1863 linecount = 0;
1864 update = false;
1865 }
1866 }
1867 if (linecount) {
1868 udl_draw_line(sc, &fb[y * sc->sc_width + x -
1869 linecount], y * sc->sc_width + x -
1870 linecount, linecount);
1871 }
1872 }
1873 memcpy(sc->sc_fbmem_prev, curfb, sc->sc_height * sc->sc_width
1874 * 2);
1875 kmem_free(curfb, UDL_FBMEM_SIZE(sc));
1876#else
1877 fb = (uint16_t *)sc->sc_fbmem;
1878 for (offs = 0; offs < sc->sc_height * sc->sc_width; offs += stride)
1879 udl_draw_line(sc, &fb[offs], offs, stride);
1880
1881#endif
1882
1883 kpause("udlslp", false, (40 * hz)/1000 + 1, &sc->sc_thread_mtx);
1884 continue;
1885
1886thread_wait:
1887 cv_wait(&sc->sc_thread_cv, &sc->sc_thread_mtx);
1888 }
1889}
1890
1891static inline void
1892udl_startstop(struct udl_softc *sc, bool stop)
1893{
1894 mutex_enter(&sc->sc_thread_mtx);
1895 sc->sc_thread_stop = stop;
1896 if (!stop)
1897 cv_broadcast(&sc->sc_thread_cv);
1898 mutex_exit(&sc->sc_thread_mtx);
1899}
1900