xref: /netbsd-src/sys/dev/pci/voyager/voyagerfb.c (revision 2dd295436a0082eb4f8d294f4aa73c223413d0f2)
1 /*	$NetBSD: voyagerfb.c,v 1.33 2021/08/07 16:19:14 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 2009, 2011 Michael Lorenz
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  * A console driver for Silicon Motion SM502 / Voyager GX  graphics controllers
30  * tested on GDIUM only so far
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: voyagerfb.c,v 1.33 2021/08/07 16:19:14 thorpej Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/device.h>
40 #include <sys/malloc.h>
41 #include <sys/lwp.h>
42 #include <sys/kauth.h>
43 
44 #include <dev/videomode/videomode.h>
45 
46 #include <dev/pci/pcivar.h>
47 #include <dev/pci/pcireg.h>
48 #include <dev/pci/pcidevs.h>
49 #include <dev/pci/pciio.h>
50 #include <dev/ic/sm502reg.h>
51 
52 #include <dev/wscons/wsdisplayvar.h>
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/wsfont/wsfont.h>
55 #include <dev/rasops/rasops.h>
56 #include <dev/wscons/wsdisplay_vconsvar.h>
57 #include <dev/pci/wsdisplay_pci.h>
58 
59 #include <dev/i2c/i2cvar.h>
60 #include <dev/pci/voyagervar.h>
61 #include <dev/wscons/wsdisplay_glyphcachevar.h>
62 
63 #include "opt_voyagerfb.h"
64 
65 #ifdef VOYAGERFB_DEBUG
66 #define DPRINTF aprint_error
67 #else
68 #define DPRINTF while (0) printf
69 #endif
70 
71 /* XXX these are gdium-specific */
72 #define GPIO_BACKLIGHT	0x20000000
73 
74 struct voyagerfb_softc {
75 	device_t sc_dev;
76 
77 	pci_chipset_tag_t sc_pc;
78 	pcitag_t sc_pcitag;
79 	bus_space_tag_t sc_memt;
80 
81 	bus_space_handle_t sc_fbh;
82 	bus_space_handle_t sc_regh;
83 	bus_addr_t sc_fb, sc_reg;
84 	bus_size_t sc_fbsize, sc_regsize;
85 
86 	int sc_width, sc_height, sc_depth, sc_stride;
87 	int sc_locked;
88 	void *sc_fbaddr;
89 	struct vcons_screen sc_console_screen;
90 	struct wsscreen_descr sc_defaultscreen_descr;
91 	const struct wsscreen_descr *sc_screens[1];
92 	struct wsscreen_list sc_screenlist;
93 	struct vcons_data vd;
94 	uint8_t *sc_dataport;
95 	int sc_mode;
96 	int sc_bl_on, sc_bl_level;
97 	void *sc_gpio_cookie;
98 
99 	/* cursor stuff */
100 	int sc_cur_x;
101 	int sc_cur_y;
102 	int sc_hot_x;
103 	int sc_hot_y;
104 	uint32_t sc_cursor_addr;
105 	uint32_t *sc_cursor;
106 
107 	/* colour map */
108 	u_char sc_cmap_red[256];
109 	u_char sc_cmap_green[256];
110 	u_char sc_cmap_blue[256];
111 
112 	glyphcache sc_gc;
113 };
114 
115 static int	voyagerfb_match(device_t, cfdata_t, void *);
116 static void	voyagerfb_attach(device_t, device_t, void *);
117 
118 CFATTACH_DECL_NEW(voyagerfb, sizeof(struct voyagerfb_softc),
119     voyagerfb_match, voyagerfb_attach, NULL, NULL);
120 
121 extern const u_char rasops_cmap[768];
122 
123 static int	voyagerfb_ioctl(void *, void *, u_long, void *, int,
124 			     struct lwp *);
125 static paddr_t	voyagerfb_mmap(void *, void *, off_t, int);
126 static void	voyagerfb_init_screen(void *, struct vcons_screen *, int,
127 		 long *);
128 
129 static int	voyagerfb_putcmap(struct voyagerfb_softc *,
130 		 struct wsdisplay_cmap *);
131 static int 	voyagerfb_getcmap(struct voyagerfb_softc *,
132 		 struct wsdisplay_cmap *);
133 static void	voyagerfb_restore_palette(struct voyagerfb_softc *);
134 static int 	voyagerfb_putpalreg(struct voyagerfb_softc *, int, uint8_t,
135 			    uint8_t, uint8_t);
136 
137 static void	voyagerfb_init(struct voyagerfb_softc *);
138 
139 static void	voyagerfb_rectfill(struct voyagerfb_softc *, int, int, int, int,
140 			    uint32_t);
141 static void	voyagerfb_bitblt(void *, int, int, int, int,
142 			    int, int, int);
143 
144 static void	voyagerfb_cursor(void *, int, int, int);
145 static void	voyagerfb_putchar_mono(void *, int, int, u_int, long);
146 static void	voyagerfb_putchar_aa32(void *, int, int, u_int, long);
147 static void	voyagerfb_putchar_aa8(void *, int, int, u_int, long);
148 static void	voyagerfb_copycols(void *, int, int, int, int);
149 static void	voyagerfb_erasecols(void *, int, int, int, long);
150 static void	voyagerfb_copyrows(void *, int, int, int);
151 static void	voyagerfb_eraserows(void *, int, int, long);
152 
153 static int	voyagerfb_set_curpos(struct voyagerfb_softc *, int, int);
154 static int	voyagerfb_gcursor(struct voyagerfb_softc *,
155 		 struct wsdisplay_cursor *);
156 static int	voyagerfb_scursor(struct voyagerfb_softc *,
157 		 struct wsdisplay_cursor *);
158 
159 struct wsdisplay_accessops voyagerfb_accessops = {
160 	voyagerfb_ioctl,
161 	voyagerfb_mmap,
162 	NULL,	/* alloc_screen */
163 	NULL,	/* free_screen */
164 	NULL,	/* show_screen */
165 	NULL, 	/* load_font */
166 	NULL,	/* pollc */
167 	NULL	/* scroll */
168 };
169 
170 static void	voyagerfb_setup_backlight(struct voyagerfb_softc *);
171 static void	voyagerfb_brightness_up(device_t);
172 static void	voyagerfb_brightness_down(device_t);
173 /* set backlight level */
174 static void	voyagerfb_set_backlight(struct voyagerfb_softc *, int);
175 /* turn backlight on and off without messing with the level */
176 static void	voyagerfb_switch_backlight(struct voyagerfb_softc *, int);
177 
178 /* wait for FIFO empty so we can feed it another command */
179 static inline void
180 voyagerfb_ready(struct voyagerfb_softc *sc)
181 {
182 	do {} while ((bus_space_read_4(sc->sc_memt, sc->sc_regh,
183 	    SM502_SYSTEM_CTRL) & SM502_SYSCTL_FIFO_EMPTY) == 0);
184 }
185 
186 /* wait for the drawing engine to be idle */
187 static inline void
188 voyagerfb_wait(struct voyagerfb_softc *sc)
189 {
190 	do {} while ((bus_space_read_4(sc->sc_memt, sc->sc_regh,
191 	    SM502_SYSTEM_CTRL) & SM502_SYSCTL_ENGINE_BUSY) != 0);
192 }
193 
194 static int
195 voyagerfb_match(device_t parent, cfdata_t match, void *aux)
196 {
197 	struct voyager_attach_args *vaa = (struct voyager_attach_args *)aux;
198 
199 	if (strcmp(vaa->vaa_name, "voyagerfb") == 0) return 100;
200 	return 0;
201 }
202 
203 static void
204 voyagerfb_attach(device_t parent, device_t self, void *aux)
205 {
206 	struct voyagerfb_softc	*sc = device_private(self);
207 	struct voyager_attach_args	*vaa = aux;
208 	struct rasops_info	*ri;
209 	struct wsemuldisplaydev_attach_args aa;
210 	prop_dictionary_t	dict;
211 	unsigned long		defattr;
212 	uint32_t		reg;
213 	bool			is_console;
214 	int i, j;
215 	uint8_t			cmap[768];
216 
217 	sc->sc_pc = vaa->vaa_pc;
218 	sc->sc_pcitag = vaa->vaa_pcitag;
219 	sc->sc_memt = vaa->vaa_tag;
220 	sc->sc_dev = self;
221 
222 	aprint_normal("\n");
223 
224 	dict = device_properties(self);
225 	prop_dictionary_get_bool(dict, "is_console", &is_console);
226 
227 	sc->sc_fb = vaa->vaa_mem_pa;
228 	sc->sc_fbh = vaa->vaa_memh;
229 	sc->sc_fbaddr = bus_space_vaddr(sc->sc_memt, sc->sc_fbh);
230 
231 	sc->sc_reg = vaa->vaa_reg_pa;
232 	sc->sc_regh = vaa->vaa_regh;
233 	sc->sc_regsize = 2 * 1024 * 1024;
234 	sc->sc_dataport = bus_space_vaddr(sc->sc_memt, sc->sc_regh);
235 	sc->sc_dataport += SM502_DATAPORT;
236 
237 	sc->sc_gpio_cookie = device_private(parent);
238 
239 	reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_DRAM_CONTROL);
240 	switch(reg & 0x0000e000) {
241 		case SM502_MEM_2M:
242 			sc->sc_fbsize = 2 * 1024 * 1024;
243 			break;
244 		case SM502_MEM_4M:
245 			sc->sc_fbsize = 4 * 1024 * 1024;
246 			break;
247 		case SM502_MEM_8M:
248 			sc->sc_fbsize = 8 * 1024 * 1024;
249 			break;
250 		case SM502_MEM_16M:
251 			sc->sc_fbsize = 16 * 1024 * 1024;
252 			break;
253 		case SM502_MEM_32M:
254 			sc->sc_fbsize = 32 * 1024 * 1024;
255 			break;
256 		case SM502_MEM_64M:
257 			sc->sc_fbsize = 64 * 1024 * 1024;
258 			break;
259 	}
260 
261 	sc->sc_width = (bus_space_read_4(sc->sc_memt, sc->sc_regh,
262 		SM502_PANEL_FB_WIDTH) & SM502_FBW_WIN_WIDTH_MASK) >> 16;
263 	sc->sc_height = (bus_space_read_4(sc->sc_memt, sc->sc_regh,
264 		SM502_PANEL_FB_HEIGHT) & SM502_FBH_WIN_HEIGHT_MASK) >> 16;
265 
266 #ifdef VOYAGERFB_DEPTH_32
267 	sc->sc_depth = 32;
268 #else
269 	sc->sc_depth = 8;
270 #endif
271 
272 	/*
273 	 * XXX yeah, casting the fb address to uint32_t is formally wrong
274 	 * but as far as I know there are no SM502 with 64bit BARs
275 	 */
276 	aprint_normal_dev(self, "%d MB video memory at 0x%08x\n",
277 	    (int)(sc->sc_fbsize >> 20), (uint32_t)sc->sc_fb);
278 
279 	/* init engine here */
280 	voyagerfb_init(sc);
281 
282 	aprint_normal_dev(self, "%d x %d, %d bit, stride %d\n",
283 		sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride);
284 
285 	sc->sc_defaultscreen_descr = (struct wsscreen_descr){
286 		"default",
287 		0, 0,
288 		NULL,
289 		8, 16,
290 		WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE |
291 		WSSCREEN_RESIZE,
292 		NULL
293 	};
294 	sc->sc_screens[0] = &sc->sc_defaultscreen_descr;
295 	sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
296 	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
297 	sc->sc_locked = 0;
298 
299 	vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr,
300 	    &voyagerfb_accessops);
301 	sc->vd.init_screen = voyagerfb_init_screen;
302 	sc->vd.show_screen_cookie = &sc->sc_gc;
303 	sc->vd.show_screen_cb = glyphcache_adapt;
304 
305 	/* backlight control */
306 	voyagerfb_setup_backlight(sc);
307 
308 	ri = &sc->sc_console_screen.scr_ri;
309 
310 	sc->sc_gc.gc_bitblt = voyagerfb_bitblt;
311 	sc->sc_gc.gc_blitcookie = sc;
312 	sc->sc_gc.gc_rop = ROP_COPY;
313 	if (is_console) {
314 		vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1,
315 		    &defattr);
316 		sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
317 
318 		sc->sc_defaultscreen_descr.textops = &ri->ri_ops;
319 		sc->sc_defaultscreen_descr.capabilities = ri->ri_caps;
320 		sc->sc_defaultscreen_descr.nrows = ri->ri_rows;
321 		sc->sc_defaultscreen_descr.ncols = ri->ri_cols;
322 	} else {
323 		if (sc->sc_console_screen.scr_ri.ri_rows == 0) {
324 			/* do some minimal setup to avoid weirdness later */
325 			vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1,
326 			    &defattr);
327 		} else
328 			(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
329 	}
330 	glyphcache_init(&sc->sc_gc, sc->sc_height,
331 			((sc->sc_fbsize - 16 * 64) / sc->sc_stride) -
332 			    sc->sc_height,
333 			sc->sc_width,
334 			ri->ri_font->fontwidth,
335 			ri->ri_font->fontheight,
336 			defattr);
337 	if (is_console)
338 		wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0,
339 		    defattr);
340 
341 	rasops_get_cmap(ri, cmap, sizeof(cmap));
342 	j = 0;
343 	if (sc->sc_depth <= 8) {
344 		for (i = 0; i < 256; i++) {
345 
346 			sc->sc_cmap_red[i] = cmap[j];
347 			sc->sc_cmap_green[i] = cmap[j + 1];
348 			sc->sc_cmap_blue[i] = cmap[j + 2];
349 			voyagerfb_putpalreg(sc, i, cmap[j], cmap[j + 1],
350 			    cmap[j + 2]);
351 			j += 3;
352 		}
353 	}
354 
355 	voyagerfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height,
356 	    ri->ri_devcmap[(defattr >> 16) & 0xff]);
357 
358 	if (is_console)
359 		vcons_replay_msgbuf(&sc->sc_console_screen);
360 
361 	aa.console = is_console;
362 	aa.scrdata = &sc->sc_screenlist;
363 	aa.accessops = &voyagerfb_accessops;
364 	aa.accesscookie = &sc->vd;
365 
366 	config_found(sc->sc_dev, &aa, wsemuldisplaydevprint,
367 	    CFARGS(.iattr = "wsemuldisplaydev"));
368 }
369 
370 static int
371 voyagerfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
372 	struct lwp *l)
373 {
374 	struct vcons_data *vd = v;
375 	struct voyagerfb_softc *sc = vd->cookie;
376 	struct wsdisplay_fbinfo *wdf;
377 	struct vcons_screen *ms = vd->active;
378 	struct wsdisplay_param  *param;
379 
380 	switch (cmd) {
381 	case WSDISPLAYIO_GTYPE:
382 		*(u_int *)data = WSDISPLAY_TYPE_PCIMISC;
383 		return 0;
384 
385 	/* PCI config read/write pass through. */
386 	case PCI_IOC_CFGREAD:
387 	case PCI_IOC_CFGWRITE:
388 		return pci_devioctl(sc->sc_pc, sc->sc_pcitag,
389 		    cmd, data, flag, l);
390 
391 	case WSDISPLAYIO_GET_BUSID:
392 		return wsdisplayio_busid_pci(device_parent(sc->sc_dev),
393 		    sc->sc_pc, sc->sc_pcitag, data);
394 
395 	case WSDISPLAYIO_GINFO:
396 		if (ms == NULL)
397 			return ENODEV;
398 		wdf = (void *)data;
399 		wdf->height = ms->scr_ri.ri_height;
400 		wdf->width = ms->scr_ri.ri_width;
401 		wdf->depth = 32;
402 		wdf->cmsize = 256;
403 		return 0;
404 
405 	case WSDISPLAYIO_GETCMAP:
406 		return voyagerfb_getcmap(sc,
407 		    (struct wsdisplay_cmap *)data);
408 
409 	case WSDISPLAYIO_PUTCMAP:
410 		return voyagerfb_putcmap(sc,
411 		    (struct wsdisplay_cmap *)data);
412 
413 	case WSDISPLAYIO_LINEBYTES:
414 		*(u_int *)data = sc->sc_stride;
415 		return 0;
416 
417 	case WSDISPLAYIO_SMODE: {
418 		int new_mode = *(int*)data;
419 		if (new_mode != sc->sc_mode) {
420 			sc->sc_mode = new_mode;
421 			if(new_mode == WSDISPLAYIO_MODE_EMUL) {
422 #ifdef VOYAGERFB_DEPTH_32
423 				sc->sc_depth = 32;
424 #else
425 				sc->sc_depth = 8;
426 #endif
427 				glyphcache_wipe(&sc->sc_gc);
428 				voyagerfb_init(sc);
429 				voyagerfb_restore_palette(sc);
430 				vcons_redraw_screen(ms);
431 			} else {
432 				sc->sc_depth = 32;
433 				voyagerfb_init(sc);
434 			}
435 		}
436 		}
437 		return 0;
438 
439 	case WSDISPLAYIO_GVIDEO:
440 		*(int *)data = sc->sc_bl_on ? WSDISPLAYIO_VIDEO_ON :
441 					      WSDISPLAYIO_VIDEO_OFF;
442 		return 0;
443 
444 	case WSDISPLAYIO_SVIDEO: {
445 			int new_bl = *(int *)data;
446 
447 			voyagerfb_switch_backlight(sc,  new_bl);
448 		}
449 		return 0;
450 
451 	case WSDISPLAYIO_GETPARAM:
452 		param = (struct wsdisplay_param *)data;
453 		switch (param->param) {
454 		case WSDISPLAYIO_PARAM_BRIGHTNESS:
455 			param->min = 0;
456 			param->max = 255;
457 			param->curval = sc->sc_bl_level;
458 			return 0;
459 		case WSDISPLAYIO_PARAM_BACKLIGHT:
460 			param->min = 0;
461 			param->max = 1;
462 			param->curval = sc->sc_bl_on;
463 			return 0;
464 		}
465 		return EPASSTHROUGH;
466 
467 	case WSDISPLAYIO_SETPARAM:
468 		param = (struct wsdisplay_param *)data;
469 		switch (param->param) {
470 		case WSDISPLAYIO_PARAM_BRIGHTNESS:
471 			voyagerfb_set_backlight(sc, param->curval);
472 			return 0;
473 		case WSDISPLAYIO_PARAM_BACKLIGHT:
474 			voyagerfb_switch_backlight(sc,  param->curval);
475 			return 0;
476 		}
477 		return EPASSTHROUGH;
478 
479 	case WSDISPLAYIO_GCURPOS:
480 		{
481 			struct wsdisplay_curpos *pos;
482 
483 			pos = (struct wsdisplay_curpos *)data;
484 			pos->x = sc->sc_cur_x;
485 			pos->y = sc->sc_cur_y;
486 		}
487 		return 0;
488 
489 	case WSDISPLAYIO_SCURPOS:
490 		{
491 			struct wsdisplay_curpos *pos;
492 
493 			pos = (struct wsdisplay_curpos *)data;
494 			voyagerfb_set_curpos(sc, pos->x, pos->y);
495 		}
496 		return 0;
497 
498 	case WSDISPLAYIO_GCURMAX:
499 		{
500 			struct wsdisplay_curpos *pos;
501 
502 			pos = (struct wsdisplay_curpos *)data;
503 			pos->x = 64;
504 			pos->y = 64;
505 		}
506 		return 0;
507 
508 	case WSDISPLAYIO_GCURSOR:
509 		{
510 			struct wsdisplay_cursor *cu;
511 
512 			cu = (struct wsdisplay_cursor *)data;
513 			return voyagerfb_gcursor(sc, cu);
514 		}
515 
516 	case WSDISPLAYIO_SCURSOR:
517 		{
518 			struct wsdisplay_cursor *cu;
519 
520 			cu = (struct wsdisplay_cursor *)data;
521 			return voyagerfb_scursor(sc, cu);
522 		}
523 
524 	case WSDISPLAYIO_GET_FBINFO:
525 		{
526 			struct wsdisplayio_fbinfo *fbi = data;
527 			return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi);
528 		}
529 	}
530 	return EPASSTHROUGH;
531 }
532 
533 static paddr_t
534 voyagerfb_mmap(void *v, void *vs, off_t offset, int prot)
535 {
536 	struct vcons_data *vd = v;
537 	struct voyagerfb_softc *sc = vd->cookie;
538 	paddr_t pa;
539 
540 	/* 'regular' framebuffer mmap()ing */
541 	if (offset < sc->sc_fbsize) {
542 		pa = bus_space_mmap(sc->sc_memt, sc->sc_fb + offset, 0, prot,
543 		    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE);
544 		return pa;
545 	}
546 
547 	/*
548 	 * restrict all other mappings to processes with privileges
549 	 */
550 	if (kauth_authorize_machdep(kauth_cred_get(),
551 	    KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) != 0) {
552 		aprint_normal("%s: mmap() rejected.\n",
553 		    device_xname(sc->sc_dev));
554 		return -1;
555 	}
556 
557 	if ((offset >= sc->sc_fb) && (offset < (sc->sc_fb + sc->sc_fbsize))) {
558 		pa = bus_space_mmap(sc->sc_memt, offset, 0, prot,
559 		    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE);
560 		return pa;
561 	}
562 
563 	if ((offset >= sc->sc_reg) &&
564 	    (offset < (sc->sc_reg + sc->sc_regsize))) {
565 		pa = bus_space_mmap(sc->sc_memt, offset, 0, prot, 0);
566 		return pa;
567 	}
568 
569 	return -1;
570 }
571 
572 static void
573 voyagerfb_init_screen(void *cookie, struct vcons_screen *scr,
574     int existing, long *defattr)
575 {
576 	struct voyagerfb_softc *sc = cookie;
577 	struct rasops_info *ri = &scr->scr_ri;
578 
579 	ri->ri_depth = sc->sc_depth;
580 	ri->ri_width = sc->sc_width;
581 	ri->ri_height = sc->sc_height;
582 	ri->ri_stride = sc->sc_stride;
583 	ri->ri_flg = RI_CENTER | RI_FULLCLEAR;
584 
585 	ri->ri_bits = (char *)sc->sc_fbaddr;
586 
587 	if (existing) {
588 		ri->ri_flg |= RI_CLEAR;
589 	}
590 
591 	if (sc->sc_depth == 8) {
592 		ri->ri_flg |= RI_8BIT_IS_RGB;
593 #ifdef VOYAGERFB_ANTIALIAS
594 		ri->ri_flg |= RI_ENABLE_ALPHA | RI_PREFER_ALPHA;
595 #endif
596 	}
597 	if (sc->sc_depth == 32) {
598 #ifdef VOYAGERFB_ANTIALIAS
599 		ri->ri_flg |= RI_ENABLE_ALPHA;
600 #endif
601 		/* we always run in RGB */
602 		ri->ri_rnum = 8;
603 		ri->ri_gnum = 8;
604 		ri->ri_bnum = 8;
605 		ri->ri_rpos = 16;
606 		ri->ri_gpos = 8;
607 		ri->ri_bpos = 0;
608 	}
609 
610 	scr->scr_flags |= VCONS_LOADFONT;
611 
612 	rasops_init(ri, 0, 0);
613 	ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE |
614 		      WSSCREEN_RESIZE;
615 
616 	rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
617 		    sc->sc_width / ri->ri_font->fontwidth);
618 
619 	ri->ri_hw = scr;
620 	ri->ri_ops.copyrows = voyagerfb_copyrows;
621 	ri->ri_ops.copycols = voyagerfb_copycols;
622 	ri->ri_ops.eraserows = voyagerfb_eraserows;
623 	ri->ri_ops.erasecols = voyagerfb_erasecols;
624 	ri->ri_ops.cursor = voyagerfb_cursor;
625 	if (FONT_IS_ALPHA(ri->ri_font)) {
626 	        switch (sc->sc_depth) {
627 	                case 32:
628                 		ri->ri_ops.putchar = voyagerfb_putchar_aa32;
629                 		break;
630                         case 8:
631                                 ri->ri_ops.putchar = voyagerfb_putchar_aa8;
632                                 break;
633                         default:
634                                 printf("alpha font at %d!?\n", sc->sc_depth);
635                 }
636 	} else
637 		ri->ri_ops.putchar = voyagerfb_putchar_mono;
638 }
639 
640 static int
641 voyagerfb_putcmap(struct voyagerfb_softc *sc, struct wsdisplay_cmap *cm)
642 {
643 	u_char *r, *g, *b;
644 	u_int index = cm->index;
645 	u_int count = cm->count;
646 	int i, error;
647 	u_char rbuf[256], gbuf[256], bbuf[256];
648 
649 #ifdef VOYAGERFB_DEBUG
650 	aprint_debug("putcmap: %d %d\n",index, count);
651 #endif
652 	if (cm->index >= 256 || cm->count > 256 ||
653 	    (cm->index + cm->count) > 256)
654 		return EINVAL;
655 	error = copyin(cm->red, &rbuf[index], count);
656 	if (error)
657 		return error;
658 	error = copyin(cm->green, &gbuf[index], count);
659 	if (error)
660 		return error;
661 	error = copyin(cm->blue, &bbuf[index], count);
662 	if (error)
663 		return error;
664 
665 	memcpy(&sc->sc_cmap_red[index], &rbuf[index], count);
666 	memcpy(&sc->sc_cmap_green[index], &gbuf[index], count);
667 	memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count);
668 
669 	r = &sc->sc_cmap_red[index];
670 	g = &sc->sc_cmap_green[index];
671 	b = &sc->sc_cmap_blue[index];
672 
673 	for (i = 0; i < count; i++) {
674 		voyagerfb_putpalreg(sc, index, *r, *g, *b);
675 		index++;
676 		r++, g++, b++;
677 	}
678 	return 0;
679 }
680 
681 static int
682 voyagerfb_getcmap(struct voyagerfb_softc *sc, struct wsdisplay_cmap *cm)
683 {
684 	u_int index = cm->index;
685 	u_int count = cm->count;
686 	int error;
687 
688 	if (index >= 255 || count > 256 || index + count > 256)
689 		return EINVAL;
690 
691 	error = copyout(&sc->sc_cmap_red[index],   cm->red,   count);
692 	if (error)
693 		return error;
694 	error = copyout(&sc->sc_cmap_green[index], cm->green, count);
695 	if (error)
696 		return error;
697 	error = copyout(&sc->sc_cmap_blue[index],  cm->blue,  count);
698 	if (error)
699 		return error;
700 
701 	return 0;
702 }
703 
704 static void
705 voyagerfb_restore_palette(struct voyagerfb_softc *sc)
706 {
707 	int i;
708 
709 	for (i = 0; i < 256; i++) {
710 		voyagerfb_putpalreg(sc, i, sc->sc_cmap_red[i],
711 		    sc->sc_cmap_green[i], sc->sc_cmap_blue[i]);
712 	}
713 }
714 
715 static int
716 voyagerfb_putpalreg(struct voyagerfb_softc *sc, int idx, uint8_t r,
717     uint8_t g, uint8_t b)
718 {
719 	uint32_t reg;
720 
721 	reg = (r << 16) | (g << 8) | b;
722 	/* XXX we should probably write the CRT palette too */
723 	bus_space_write_4(sc->sc_memt, sc->sc_regh,
724 	    SM502_PALETTE_PANEL + (idx << 2), reg);
725 	return 0;
726 }
727 
728 static void
729 voyagerfb_init(struct voyagerfb_softc *sc)
730 {
731 	int reg;
732 
733 	voyagerfb_wait(sc);
734 	/* disable colour compare */
735 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_COLOR_COMP_MASK, 0);
736 	/* allow writes to all planes */
737 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PLANEMASK,
738 	    0xffffffff);
739 	/* disable clipping */
740 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CLIP_TOP_LEFT, 0);
741 	/* source and destination in local memory, no offset */
742 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC_BASE, 0);
743 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST_BASE, 0);
744 	/* pitch is screen stride */
745 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PITCH,
746 	    sc->sc_width | (sc->sc_width << 16));
747 	/* window is screen width */
748 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_WINDOW_WIDTH,
749 	    sc->sc_width | (sc->sc_width << 16));
750 	reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL);
751 	reg &= ~SM502_PDC_DEPTH_MASK;
752 
753 	switch (sc->sc_depth) {
754 		case 8:
755 			bus_space_write_4(sc->sc_memt, sc->sc_regh,
756 			    SM502_STRETCH, SM502_STRETCH_8BIT);
757 			sc->sc_stride = sc->sc_width;
758 			reg |= SM502_PDC_8BIT;
759 			break;
760 		case 16:
761 			bus_space_write_4(sc->sc_memt, sc->sc_regh,
762 			    SM502_STRETCH, SM502_STRETCH_16BIT);
763 			sc->sc_stride = sc->sc_width << 1;
764 			reg |= SM502_PDC_16BIT;
765 			break;
766 		case 24:
767 		case 32:
768 			bus_space_write_4(sc->sc_memt, sc->sc_regh,
769 			    SM502_STRETCH, SM502_STRETCH_32BIT);
770 			sc->sc_stride = sc->sc_width << 2;
771 			reg |= SM502_PDC_32BIT;
772 			break;
773 	}
774 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_FB_OFFSET,
775 	    (sc->sc_stride << 16) | sc->sc_stride);
776 
777 	/* clear the screen... */
778 	voyagerfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height, 0);
779 
780 	/* ...and then switch colour depth. For aesthetic reasons. */
781 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL,
782 	    reg);
783 
784 	/* put the cursor at the end of video memory */
785 	sc->sc_cursor_addr = sc->sc_fbsize - 16 * 64;	/* XXX */
786 	DPRINTF("%s: %08x\n", __func__, sc->sc_cursor_addr);
787 	sc->sc_cursor = (uint32_t *)((uint8_t *)bus_space_vaddr(sc->sc_memt,
788 			 sc->sc_fbh) + sc->sc_cursor_addr);
789 #ifdef VOYAGERFB_DEBUG
790 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_XY,
791 							 0x00100010);
792 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_COL12,
793 							 0x0000ffff);
794 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_COL3,
795 							 0x0000f800);
796 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_ADDR,
797 	    SM502_CRSR_ENABLE | sc->sc_cursor_addr);
798 	sc->sc_cursor[0] = 0x00000000;
799 	sc->sc_cursor[1] = 0x00000000;
800 	sc->sc_cursor[2] = 0xffffffff;
801 	sc->sc_cursor[3] = 0xffffffff;
802 	sc->sc_cursor[4] = 0xaaaaaaaa;
803 	sc->sc_cursor[5] = 0xaaaaaaaa;
804 	sc->sc_cursor[6] = 0xffffffff;
805 	sc->sc_cursor[7] = 0x00000000;
806 #else
807 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_ADDR,
808 	    sc->sc_cursor_addr);
809 #endif
810 }
811 
812 static void
813 voyagerfb_rectfill(struct voyagerfb_softc *sc, int x, int y, int wi, int he,
814      uint32_t colour)
815 {
816 
817 	voyagerfb_ready(sc);
818 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL,
819 	    ROP_COPY |
820 	    SM502_CTRL_USE_ROP2 |
821 	    SM502_CTRL_CMD_RECTFILL |
822 	    SM502_CTRL_QUICKSTART_E);
823 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_FOREGROUND,
824 	    colour);
825 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST,
826 	    (x << 16) | y);
827 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION,
828 	    (wi << 16) | he);
829 }
830 
831 static void
832 voyagerfb_bitblt(void *cookie, int xs, int ys, int xd, int yd,
833     int wi, int he, int rop)
834 {
835 	struct voyagerfb_softc *sc = cookie;
836 	uint32_t cmd;
837 
838 	cmd = (rop & 0xf) | SM502_CTRL_USE_ROP2 | SM502_CTRL_CMD_BITBLT |
839 	      SM502_CTRL_QUICKSTART_E;
840 
841 	voyagerfb_ready(sc);
842 
843 	if (xd <= xs) {
844 		/* left to right */
845 	} else {
846 		/*
847 		 * According to the manual this flag reverses only the blitter's
848 		 * X direction. At least on my Gdium it also reverses the Y
849 		 * direction
850 		 */
851 		cmd |= SM502_CTRL_R_TO_L;
852 		xs += wi - 1;
853 		xd += wi - 1;
854 		ys += he - 1;
855 		yd += he - 1;
856 	}
857 
858 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd);
859 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC,
860 	    (xs << 16) | ys);
861 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST,
862 	    (xd << 16) | yd);
863 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION,
864 	    (wi << 16) | he);
865 }
866 
867 static void
868 voyagerfb_cursor(void *cookie, int on, int row, int col)
869 {
870 	struct rasops_info *ri = cookie;
871 	struct vcons_screen *scr = ri->ri_hw;
872 	struct voyagerfb_softc *sc = scr->scr_cookie;
873 	int x, y, wi, he;
874 
875 	wi = ri->ri_font->fontwidth;
876 	he = ri->ri_font->fontheight;
877 
878 	if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
879 		x = ri->ri_ccol * wi + ri->ri_xorigin;
880 		y = ri->ri_crow * he + ri->ri_yorigin;
881 		if (ri->ri_flg & RI_CURSOR) {
882 			voyagerfb_bitblt(sc, x, y, x, y, wi, he, ROP_INVERT);
883 			ri->ri_flg &= ~RI_CURSOR;
884 		}
885 		ri->ri_crow = row;
886 		ri->ri_ccol = col;
887 		if (on) {
888 			x = ri->ri_ccol * wi + ri->ri_xorigin;
889 			y = ri->ri_crow * he + ri->ri_yorigin;
890 			voyagerfb_bitblt(sc, x, y, x, y, wi, he, ROP_INVERT);
891 			ri->ri_flg |= RI_CURSOR;
892 		}
893 	} else {
894 		scr->scr_ri.ri_crow = row;
895 		scr->scr_ri.ri_ccol = col;
896 		scr->scr_ri.ri_flg &= ~RI_CURSOR;
897 	}
898 
899 }
900 
901 static inline void
902 voyagerfb_feed8(struct voyagerfb_softc *sc, uint8_t *data, int len)
903 {
904 	uint32_t *port = (uint32_t *)sc->sc_dataport;
905 	int i;
906 
907 	for (i = 0; i < ((len + 3) & 0xfffc); i++) {
908 		*port = *data;
909 		data++;
910 	}
911 }
912 
913 static inline void
914 voyagerfb_feed16(struct voyagerfb_softc *sc, uint16_t *data, int len)
915 {
916 	uint32_t *port = (uint32_t *)sc->sc_dataport;
917 	int i;
918 
919 	len = len << 1;
920 	for (i = 0; i < ((len + 1) & 0xfffe); i++) {
921 		*port = *data;
922 		data++;
923 	}
924 }
925 
926 static void
927 voyagerfb_putchar_mono(void *cookie, int row, int col, u_int c, long attr)
928 {
929 	struct rasops_info *ri = cookie;
930 	struct wsdisplay_font *font = PICK_FONT(ri, c);
931 	struct vcons_screen *scr = ri->ri_hw;
932 	struct voyagerfb_softc *sc = scr->scr_cookie;
933 	uint32_t cmd;
934 	int fg, bg;
935 	uint8_t *data;
936 	int x, y, wi, he;
937 
938 	if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL)
939 		return;
940 
941 	if (!CHAR_IN_FONT(c, font))
942 		return;
943 
944 	wi = font->fontwidth;
945 	he = font->fontheight;
946 
947 	bg = ri->ri_devcmap[(attr >> 16) & 0x0f];
948 	fg = ri->ri_devcmap[(attr >> 24) & 0x0f];
949 	x = ri->ri_xorigin + col * wi;
950 	y = ri->ri_yorigin + row * he;
951 	if (c == 0x20) {
952 		voyagerfb_rectfill(sc, x, y, wi, he, bg);
953 		return;
954 	}
955 
956 	data = WSFONT_GLYPH(c, font);
957 
958 	cmd = ROP_COPY |
959 	      SM502_CTRL_USE_ROP2 |
960 	      SM502_CTRL_CMD_HOSTWRT |
961 	      SM502_CTRL_HOSTBLT_MONO |
962 	      SM502_CTRL_QUICKSTART_E |
963 	      SM502_CTRL_MONO_PACK_32BIT;
964 	voyagerfb_ready(sc);
965 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd);
966 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_FOREGROUND, fg);
967 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_BACKGROUND, bg);
968 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC, 0);
969 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST, (x << 16) | y);
970 	bus_space_write_4(sc->sc_memt, sc->sc_regh,
971 	    SM502_DIMENSION, (wi << 16) | he);
972 	/* now feed the data, padded to 32bit */
973 	switch (ri->ri_font->stride) {
974 		case 1:
975 			voyagerfb_feed8(sc, data, ri->ri_fontscale);
976 			break;
977 		case 2:
978 			voyagerfb_feed16(sc, (uint16_t *)data,
979 			    ri->ri_fontscale);
980 			break;
981 	}
982 }
983 
984 static void
985 voyagerfb_putchar_aa32(void *cookie, int row, int col, u_int c, long attr)
986 {
987 	struct rasops_info *ri = cookie;
988 	struct wsdisplay_font *font = PICK_FONT(ri, c);
989 	struct vcons_screen *scr = ri->ri_hw;
990 	struct voyagerfb_softc *sc = scr->scr_cookie;
991 	uint32_t cmd;
992 	int fg, bg;
993 	uint8_t *data;
994 	int x, y, wi, he;
995 	int i, j, r, g, b, aval, pad;
996 	int rf, gf, bf, rb, gb, bb;
997 	uint32_t pixel;
998 	int rv;
999 
1000 	if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL)
1001 		return;
1002 
1003 	if (!CHAR_IN_FONT(c, font))
1004 		return;
1005 
1006 	wi = font->fontwidth;
1007 	he = font->fontheight;
1008 
1009 	bg = ri->ri_devcmap[(attr >> 16) & 0x0f];
1010 	fg = ri->ri_devcmap[(attr >> 24) & 0x0f];
1011 	x = ri->ri_xorigin + col * wi;
1012 	y = ri->ri_yorigin + row * he;
1013 	if (c == 0x20) {
1014 		voyagerfb_rectfill(sc, x, y, wi, he, bg);
1015 		return;
1016 	}
1017 
1018 	data = WSFONT_GLYPH(c, font);
1019 	/*
1020 	 * we can't accelerate the actual alpha blending but
1021 	 * we can at least use a host blit to go through the
1022 	 * pipeline instead of having to sync the engine
1023 	 */
1024 
1025 	rv = glyphcache_try(&sc->sc_gc, c, x, y, attr);
1026 	if (rv == GC_OK)
1027 		return;
1028 
1029 	cmd = ROP_COPY |
1030 	      SM502_CTRL_USE_ROP2 |
1031 	      SM502_CTRL_CMD_HOSTWRT |
1032 	      SM502_CTRL_QUICKSTART_E;
1033 	voyagerfb_ready(sc);
1034 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd);
1035 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC, 0);
1036 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST, (x << 16) | y);
1037 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION,
1038 	    (wi << 16) | he);
1039 	rf = (fg >> 16) & 0xff;
1040 	rb = (bg >> 16) & 0xff;
1041 	gf = (fg >> 8) & 0xff;
1042 	gb = (bg >> 8) & 0xff;
1043 	bf =  fg & 0xff;
1044 	bb =  bg & 0xff;
1045 	pad = wi & 1;
1046 	for (i = 0; i < he; i++) {
1047 		for (j = 0; j < wi; j++) {
1048 			aval = *data;
1049 			data++;
1050 			if (aval == 0) {
1051 				pixel = bg;
1052 			} else if (aval == 255) {
1053 				pixel = fg;
1054 			} else {
1055 				r = aval * rf + (255 - aval) * rb;
1056 				g = aval * gf + (255 - aval) * gb;
1057 				b = aval * bf + (255 - aval) * bb;
1058 				pixel = (r & 0xff00) << 8 |
1059 				        (g & 0xff00) |
1060 				        (b & 0xff00) >> 8;
1061 			}
1062 			bus_space_write_4(sc->sc_memt, sc->sc_regh,
1063 			    SM502_DATAPORT, pixel);
1064 		}
1065 		if (pad)
1066 			bus_space_write_4(sc->sc_memt, sc->sc_regh,
1067 			    SM502_DATAPORT, 0);
1068 	}
1069 	if (rv == GC_ADD) {
1070 		glyphcache_add(&sc->sc_gc, c, x, y);
1071 	}
1072 }
1073 
1074 static void
1075 voyagerfb_putchar_aa8(void *cookie, int row, int col, u_int c, long attr)
1076 {
1077 	struct rasops_info *ri = cookie;
1078 	struct wsdisplay_font *font = PICK_FONT(ri, c);
1079 	struct vcons_screen *scr = ri->ri_hw;
1080 	struct voyagerfb_softc *sc = scr->scr_cookie;
1081 	uint32_t cmd;
1082 	int bg;
1083 	uint8_t *data;
1084 	int x, y, wi, he;
1085 	int i, j, r, g, b, aval, pad;
1086 	int r1, g1, b1, r0, g0, b0, fgo, bgo;
1087 	uint32_t pixel = 0, latch = 0, bg8, fg8;
1088 	int rv;
1089 
1090 	if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL)
1091 		return;
1092 
1093 	if (!CHAR_IN_FONT(c, font))
1094 		return;
1095 
1096 	wi = font->fontwidth;
1097 	he = font->fontheight;
1098 
1099 	bg = ri->ri_devcmap[(attr >> 16) & 0x0f];
1100 	x = ri->ri_xorigin + col * wi;
1101 	y = ri->ri_yorigin + row * he;
1102 	if (c == 0x20) {
1103 		voyagerfb_rectfill(sc, x, y, wi, he, bg);
1104 		return;
1105 	}
1106 
1107 	data = WSFONT_GLYPH(c, font);
1108 	/*
1109 	 * we can't accelerate the actual alpha blending but
1110 	 * we can at least use a host blit to go through the
1111 	 * pipeline instead of having to sync the engine
1112 	 */
1113 
1114 	rv = glyphcache_try(&sc->sc_gc, c, x, y, attr);
1115 	if (rv == GC_OK)
1116 		return;
1117 
1118 	cmd = ROP_COPY |
1119 	      SM502_CTRL_USE_ROP2 |
1120 	      SM502_CTRL_CMD_HOSTWRT |
1121 	      SM502_CTRL_QUICKSTART_E;
1122 	voyagerfb_ready(sc);
1123 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd);
1124 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC, 0);
1125 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST, (x << 16) | y);
1126 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION,
1127 	    (wi << 16) | he);
1128 
1129 	/*
1130 	 * we need the RGB colours here, so get offsets into rasops_cmap
1131 	 */
1132 	fgo = ((attr >> 24) & 0xf) * 3;
1133 	bgo = ((attr >> 16) & 0xf) * 3;
1134 
1135 	r0 = rasops_cmap[bgo];
1136 	r1 = rasops_cmap[fgo];
1137 	g0 = rasops_cmap[bgo + 1];
1138 	g1 = rasops_cmap[fgo + 1];
1139 	b0 = rasops_cmap[bgo + 2];
1140 	b1 = rasops_cmap[fgo + 2];
1141 #define R3G3B2(r, g, b) ((r & 0xe0) | ((g >> 3) & 0x1c) | (b >> 6))
1142 	bg8 = R3G3B2(r0, g0, b0);
1143 	fg8 = R3G3B2(r1, g1, b1);
1144 
1145 	pad = wi & 4;
1146 	for (i = 0; i < he; i++) {
1147 		for (j = 0; j < wi; j++) {
1148 			aval = *data;
1149 			data++;
1150 			if (aval == 0) {
1151 				pixel = bg8;
1152 			} else if (aval == 255) {
1153 				pixel = fg8;
1154 			} else {
1155 				r = aval * r1 + (255 - aval) * r0;
1156 				g = aval * g1 + (255 - aval) * g0;
1157 				b = aval * b1 + (255 - aval) * b0;
1158 				pixel = ((r & 0xe000) >> 8) |
1159 					((g & 0xe000) >> 11) |
1160 					((b & 0xc000) >> 14);
1161 			}
1162 			latch = (latch << 8) | pixel;
1163 			/* write in 32bit chunks */
1164 			if ((j & 3) == 3) {
1165 				bus_space_write_4(sc->sc_memt, sc->sc_regh,
1166 				    SM502_DATAPORT, be32toh(latch));
1167 				latch = 0;
1168 			}
1169 		}
1170 		/* if we have pixels left in latch write them out */
1171 		if ((j & 3) != 0) {
1172 			latch = latch << ((4 - (i & 3)) << 3);
1173 			bus_space_write_4(sc->sc_memt, sc->sc_regh,
1174 			    SM502_DATAPORT, be32toh(latch));
1175 		}
1176 		if (pad)
1177 			bus_space_write_4(sc->sc_memt, sc->sc_regh,
1178 			    SM502_DATAPORT, 0);
1179 	}
1180 	if (rv == GC_ADD) {
1181 		glyphcache_add(&sc->sc_gc, c, x, y);
1182 	}
1183 }
1184 
1185 static void
1186 voyagerfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
1187 {
1188 	struct rasops_info *ri = cookie;
1189 	struct vcons_screen *scr = ri->ri_hw;
1190 	struct voyagerfb_softc *sc = scr->scr_cookie;
1191 	int32_t xs, xd, y, width, height;
1192 
1193 	if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) {
1194 		xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
1195 		xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
1196 		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1197 		width = ri->ri_font->fontwidth * ncols;
1198 		height = ri->ri_font->fontheight;
1199 		voyagerfb_bitblt(sc, xs, y, xd, y, width, height, ROP_COPY);
1200 	}
1201 }
1202 
1203 static void
1204 voyagerfb_erasecols(void *cookie, int row, int startcol, int ncols,
1205      long fillattr)
1206 {
1207 	struct rasops_info *ri = cookie;
1208 	struct vcons_screen *scr = ri->ri_hw;
1209 	struct voyagerfb_softc *sc = scr->scr_cookie;
1210 	int32_t x, y, width, height, fg, bg, ul;
1211 
1212 	if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) {
1213 		x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
1214 		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1215 		width = ri->ri_font->fontwidth * ncols;
1216 		height = ri->ri_font->fontheight;
1217 		rasops_unpack_attr(fillattr, &fg, &bg, &ul);
1218 
1219 		voyagerfb_rectfill(sc, x, y, width, height, ri->ri_devcmap[bg]);
1220 	}
1221 }
1222 
1223 static void
1224 voyagerfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
1225 {
1226 	struct rasops_info *ri = cookie;
1227 	struct vcons_screen *scr = ri->ri_hw;
1228 	struct voyagerfb_softc *sc = scr->scr_cookie;
1229 	int32_t x, ys, yd, width, height;
1230 	int i;
1231 
1232 	if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) {
1233 		x = ri->ri_xorigin;
1234 		ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
1235 		yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
1236 		width = ri->ri_emuwidth;
1237 		height = ri->ri_font->fontheight * nrows;
1238 		if ((nrows > 1) && (dstrow > srcrow)) {
1239 			/*
1240 			 * the blitter can't do bottom-up copies so we have
1241 			 * to copy line by line here
1242 			 * should probably use a command sequence
1243 			 */
1244 			ys += (height - ri->ri_font->fontheight);
1245 			yd += (height - ri->ri_font->fontheight);
1246 			for (i = 0; i < nrows; i++) {
1247 				voyagerfb_bitblt(sc, x, ys, x, yd, width,
1248 				    ri->ri_font->fontheight, ROP_COPY);
1249 				ys -= ri->ri_font->fontheight;
1250 				yd -= ri->ri_font->fontheight;
1251 			}
1252 		} else
1253 			voyagerfb_bitblt(sc, x, ys, x, yd, width, height,
1254 			    ROP_COPY);
1255 	}
1256 }
1257 
1258 static void
1259 voyagerfb_eraserows(void *cookie, int row, int nrows, long fillattr)
1260 {
1261 	struct rasops_info *ri = cookie;
1262 	struct vcons_screen *scr = ri->ri_hw;
1263 	struct voyagerfb_softc *sc = scr->scr_cookie;
1264 	int32_t x, y, width, height, fg, bg, ul;
1265 
1266 	if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) {
1267 		x = ri->ri_xorigin;
1268 		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
1269 		width = ri->ri_emuwidth;
1270 		height = ri->ri_font->fontheight * nrows;
1271 		rasops_unpack_attr(fillattr, &fg, &bg, &ul);
1272 
1273 		voyagerfb_rectfill(sc, x, y, width, height, ri->ri_devcmap[bg]);
1274 	}
1275 }
1276 
1277 /* backlight control */
1278 static void
1279 voyagerfb_setup_backlight(struct voyagerfb_softc *sc)
1280 {
1281 	/* switch the pin to gpio mode if it isn't already */
1282 	voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
1283 	/* turn it on */
1284 	voyager_write_gpio(sc->sc_gpio_cookie, 0xffffffff, GPIO_BACKLIGHT);
1285 	sc->sc_bl_on = 1;
1286 	sc->sc_bl_level = 255;
1287 	pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_UP,
1288 	    voyagerfb_brightness_up, TRUE);
1289 	pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_DOWN,
1290 	    voyagerfb_brightness_down, TRUE);
1291 }
1292 
1293 static void
1294 voyagerfb_set_backlight(struct voyagerfb_softc *sc, int level)
1295 {
1296 
1297 	/*
1298 	 * should we do nothing when backlight is off, should we just store the
1299 	 * level and use it when turning back on or should we just flip sc_bl_on
1300 	 * and turn the backlight on?
1301 	 * For now turn it on so a crashed screensaver can't get the user stuck
1302 	 * with a dark screen as long as hotkeys work
1303 	 */
1304 	if (level > 255) level = 255;
1305 	if (level < 0) level = 0;
1306 	if (level == sc->sc_bl_level)
1307 		return;
1308 	sc->sc_bl_level = level;
1309 	if (sc->sc_bl_on == 0)
1310 		sc->sc_bl_on = 1;
1311 	/* and here we would actually muck with the hardware */
1312 	if ((level == 0) || (level == 255)) {
1313 		/* in these cases bypass the PWM and use the gpio */
1314 		voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
1315 		if (level == 0) {
1316 			voyager_write_gpio(sc->sc_gpio_cookie,
1317 			    ~GPIO_BACKLIGHT, 0);
1318 		} else {
1319 			voyager_write_gpio(sc->sc_gpio_cookie,
1320 			    0xffffffff, GPIO_BACKLIGHT);
1321 		}
1322 	} else {
1323 		uint32_t pwm;
1324 
1325 		pwm = voyager_set_pwm(20000, level * 1000 / 256);
1326 		pwm |= SM502_PWM_ENABLE;
1327 		bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PWM0, pwm);
1328 
1329 		/* let the PWM take over */
1330 		voyager_control_gpio(sc->sc_gpio_cookie,
1331 		    0xffffffff, GPIO_BACKLIGHT);
1332 	}
1333 }
1334 
1335 static void
1336 voyagerfb_switch_backlight(struct voyagerfb_softc *sc, int on)
1337 {
1338 
1339 	if (on == sc->sc_bl_on)
1340 		return;
1341 	sc->sc_bl_on = on;
1342 	if (on) {
1343 		int level = sc->sc_bl_level;
1344 
1345 		sc->sc_bl_level = -1;
1346 		voyagerfb_set_backlight(sc, level);
1347 	} else {
1348 		voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
1349 		voyager_write_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
1350 	}
1351 }
1352 
1353 
1354 static void
1355 voyagerfb_brightness_up(device_t dev)
1356 {
1357 	struct voyagerfb_softc *sc = device_private(dev);
1358 
1359 	voyagerfb_set_backlight(sc, sc->sc_bl_level + 8);
1360 }
1361 
1362 static void
1363 voyagerfb_brightness_down(device_t dev)
1364 {
1365 	struct voyagerfb_softc *sc = device_private(dev);
1366 
1367 	voyagerfb_set_backlight(sc, sc->sc_bl_level - 8);
1368 }
1369 
1370 static int
1371 voyagerfb_set_curpos(struct voyagerfb_softc *sc, int x, int y)
1372 {
1373 	uint32_t val;
1374 	int xx, yy;
1375 
1376 	sc->sc_cur_x = x;
1377 	sc->sc_cur_y = y;
1378 
1379 	xx = x - sc->sc_hot_x;
1380 	yy = y - sc->sc_hot_y;
1381 
1382 	if (xx < 0) xx = abs(xx) | 0x800;
1383 	if (yy < 0) yy = abs(yy) | 0x800;
1384 
1385 	val = (xx & 0xffff) | (yy << 16);
1386 	bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_XY, val);
1387 
1388 	return 0;
1389 }
1390 
1391 static int
1392 voyagerfb_gcursor(struct voyagerfb_softc *sc, struct wsdisplay_cursor *cur)
1393 {
1394 	/* do nothing for now */
1395 	return 0;
1396 }
1397 
1398 static int
1399 voyagerfb_scursor(struct voyagerfb_softc *sc, struct wsdisplay_cursor *cur)
1400 {
1401 	if (cur->which & WSDISPLAY_CURSOR_DOCUR) {
1402 
1403 		bus_space_write_4(sc->sc_memt, sc->sc_regh,
1404 		    SM502_PANEL_CRSR_ADDR,
1405 		    sc->sc_cursor_addr | (cur->enable ? SM502_CRSR_ENABLE : 0));
1406 		DPRINTF("%s: %08x\n", __func__, sc->sc_cursor_addr);
1407 	}
1408 	if (cur->which & WSDISPLAY_CURSOR_DOHOT) {
1409 
1410 		sc->sc_hot_x = cur->hot.x;
1411 		sc->sc_hot_y = cur->hot.y;
1412 	}
1413 	if (cur->which & WSDISPLAY_CURSOR_DOPOS) {
1414 
1415 		voyagerfb_set_curpos(sc, cur->pos.x, cur->pos.y);
1416 	}
1417 	if (cur->which & WSDISPLAY_CURSOR_DOCMAP) {
1418 		int i, idx;
1419 		uint32_t val;
1420 
1421 		for (i = 0; i < cur->cmap.count; i++) {
1422 			val = ((cur->cmap.red[i] & 0xf8) << 8) |
1423 			      ((cur->cmap.green[i] & 0xfc) << 3) |
1424 			      ((cur->cmap.blue[i] & 0xf8) >> 3);
1425 			idx = i + cur->cmap.index;
1426 			bus_space_write_2(sc->sc_memt, sc->sc_regh,
1427 			    SM502_PANEL_CRSR_COL12 + (idx << 1),
1428 			    val);
1429 			/*
1430 			 * if userland doesn't try to set the 3rd colour we
1431 			 * assume it expects an X11-style 2 colour cursor
1432 			 * X should be our main user anyway
1433 			 */
1434 			if ((idx == 1) &&
1435 			   ((cur->cmap.count + cur->cmap.index) < 3)) {
1436 				bus_space_write_2(sc->sc_memt, sc->sc_regh,
1437 				    SM502_PANEL_CRSR_COL3,
1438 				    val);
1439 			}
1440 			DPRINTF("%s: %d %04x\n", __func__, i + cur->cmap.index,
1441 			    val);
1442 		}
1443 	}
1444 	if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) {
1445 
1446 		int i, j, cnt = 0;
1447 		uint32_t latch = 0, omask;
1448 		uint8_t imask;
1449 		DPRINTF("%s: %d %d\n", __func__, cur->size.x, cur->size.y);
1450 		for (i = 0; i < 256; i++) {
1451 			omask = 0x00000001;
1452 			imask = 0x01;
1453 			cur->image[cnt] &= cur->mask[cnt];
1454 			for (j = 0; j < 8; j++) {
1455 				if (cur->mask[cnt] & imask)
1456 					latch |= omask;
1457 				omask <<= 1;
1458 				if (cur->image[cnt] & imask)
1459 					latch |= omask;
1460 				omask <<= 1;
1461 				imask <<= 1;
1462 			}
1463 			cnt++;
1464 			imask = 0x01;
1465 			cur->image[cnt] &= cur->mask[cnt];
1466 			for (j = 0; j < 8; j++) {
1467 				if (cur->mask[cnt] & imask)
1468 					latch |= omask;
1469 				omask <<= 1;
1470 				if (cur->image[cnt] & imask)
1471 					latch |= omask;
1472 				omask <<= 1;
1473 				imask <<= 1;
1474 			}
1475 			cnt++;
1476 			sc->sc_cursor[i] = latch;
1477 			latch = 0;
1478 		}
1479 	}
1480 	return 0;
1481 }
1482