xref: /netbsd-src/sys/arch/sparc64/dev/ffb.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: ffb.c,v 1.38 2010/09/21 03:31:04 macallan Exp $	*/
2 /*	$OpenBSD: creator.c,v 1.20 2002/07/30 19:48:15 jason Exp $	*/
3 
4 /*
5  * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by Jason L. Wright
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
26  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: ffb.c,v 1.38 2010/09/21 03:31:04 macallan Exp $");
37 
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/device.h>
43 #include <sys/conf.h>
44 #include <sys/ioctl.h>
45 #include <sys/malloc.h>
46 #include <sys/mman.h>
47 
48 #include <machine/bus.h>
49 #include <machine/autoconf.h>
50 #include <machine/openfirm.h>
51 #include <machine/vmparam.h>
52 
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/sun/fbio.h>
55 #include <dev/sun/fbvar.h>
56 
57 #include <dev/wsfont/wsfont.h>
58 #include <dev/wscons/wsdisplay_vconsvar.h>
59 
60 #include <sparc64/dev/ffbreg.h>
61 #include <sparc64/dev/ffbvar.h>
62 
63 #include "opt_wsdisplay_compat.h"
64 #include "opt_ffb.h"
65 
66 #ifndef WS_DEFAULT_BG
67 /* Sun -> background should be white */
68 #define WS_DEFAULT_BG 0xf
69 #endif
70 
71 #ifdef FFB_SYNC
72 #define SYNC ffb_ras_wait(sc)
73 #else
74 #define SYNC
75 #endif
76 
77 extern struct cfdriver ffb_cd;
78 
79 struct wsscreen_descr ffb_stdscreen = {
80 	"sunffb",
81 	0, 0,	/* will be filled in -- XXX shouldn't, it's global. */
82 	0,
83 	0, 0,
84 	WSSCREEN_REVERSE | WSSCREEN_WSCOLORS,
85 	NULL	/* modecookie */
86 };
87 
88 const struct wsscreen_descr *ffb_scrlist[] = {
89 	&ffb_stdscreen,
90 	/* XXX other formats? */
91 };
92 
93 struct wsscreen_list ffb_screenlist = {
94 	sizeof(ffb_scrlist) / sizeof(struct wsscreen_descr *),
95 	    ffb_scrlist
96 };
97 
98 static struct vcons_screen ffb_console_screen;
99 
100 int	ffb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
101 static int ffb_blank(struct ffb_softc *, u_long, u_int *);
102 paddr_t ffb_mmap(void *, void *, off_t, int);
103 void	ffb_ras_fifo_wait(struct ffb_softc *, int);
104 void	ffb_ras_wait(struct ffb_softc *);
105 void	ffb_ras_init(struct ffb_softc *);
106 void	ffb_ras_copyrows(void *, int, int, int);
107 void	ffb_ras_erasecols(void *, int, int, int, long int);
108 void	ffb_ras_eraserows(void *, int, int, long int);
109 void	ffb_ras_do_cursor(struct rasops_info *);
110 void	ffb_ras_fill(struct ffb_softc *);
111 static void	ffb_ras_setfg(struct ffb_softc *, int32_t);
112 static void	ffb_ras_setbg(struct ffb_softc *, int32_t);
113 
114 void	ffb_clearscreen(struct ffb_softc *);
115 int	ffb_load_font(void *, void *, struct wsdisplay_font *);
116 void	ffb_init_screen(void *, struct vcons_screen *, int,
117 	    long *);
118 int	ffb_allocattr(void *, int, int, int, long *);
119 void	ffb_putchar(void *, int, int, u_int, long);
120 void	ffb_cursor(void *, int, int, int);
121 
122 /* frame buffer generic driver */
123 static void ffbfb_unblank(struct device*);
124 dev_type_open(ffbfb_open);
125 dev_type_close(ffbfb_close);
126 dev_type_ioctl(ffbfb_ioctl);
127 dev_type_mmap(ffbfb_mmap);
128 
129 static struct fbdriver ffb_fbdriver = {
130         ffbfb_unblank, ffbfb_open, ffbfb_close, ffbfb_ioctl, nopoll,
131 	ffbfb_mmap, nokqfilter
132 };
133 
134 struct wsdisplay_accessops ffb_accessops = {
135 	.ioctl = ffb_ioctl,
136 	.mmap = ffb_mmap,
137 };
138 
139 void
140 ffb_attach(struct ffb_softc *sc)
141 {
142 	struct wsemuldisplaydev_attach_args waa;
143 	struct rasops_info *ri;
144 	long defattr;
145 	const char *model;
146 	int btype;
147 	uint32_t dac;
148 	int maxrow, maxcol;
149 	u_int blank = WSDISPLAYIO_VIDEO_ON;
150 	char buf[6+1];
151 
152 	printf(":");
153 
154 	if (sc->sc_type == FFB_CREATOR) {
155 		btype = prom_getpropint(sc->sc_node, "board_type", 0);
156 		if ((btype & 7) == 3)
157 			printf(" Creator3D");
158 		else
159 			printf(" Creator");
160 	} else
161 		printf(" Elite3D");
162 
163 	model = prom_getpropstring(sc->sc_node, "model");
164 	if (model == NULL || strlen(model) == 0)
165 		model = "unknown";
166 
167 	sc->sc_depth = 24;
168 	sc->sc_linebytes = 8192;
169 	sc->sc_height = prom_getpropint(sc->sc_node, "height", 0);
170 	sc->sc_width = prom_getpropint(sc->sc_node, "width", 0);
171 
172 	sc->sc_locked = 0;
173 	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
174 
175 	maxcol = (prom_getoption("screen-#columns", buf, sizeof buf) == 0)
176 		? strtoul(buf, NULL, 10)
177 		: 80;
178 
179 	maxrow = (prom_getoption("screen-#rows", buf, sizeof buf) != 0)
180 		? strtoul(buf, NULL, 10)
181 		: 34;
182 
183 	ffb_ras_init(sc);
184 
185 	/* collect DAC version, as Elite3D cursor enable bit is reversed */
186 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_GVERS);
187 	dac = DAC_READ(sc, FFB_DAC_VALUE);
188 	sc->sc_dacrev = (dac >> 28) & 0xf;
189 
190 	if (sc->sc_type == FFB_AFB) {
191 		sc->sc_dacrev = 10;
192 		sc->sc_needredraw = 0;
193 	} else {
194 		/* see what kind of DAC we have */
195 		int pnum = (dac & 0x0ffff000) >> 12;
196 		if (pnum == 0x236e) {
197 			sc->sc_needredraw = 0;
198 		} else {
199 			sc->sc_needredraw = 1;
200 		}
201 	}
202 	printf(", model %s, dac %u\n", model, sc->sc_dacrev);
203 	if (sc->sc_needredraw)
204 		printf("%s: found old DAC, enabling redraw on unblank\n",
205 		    device_xname(&sc->sc_dv));
206 
207 	ffb_blank(sc, WSDISPLAYIO_SVIDEO, &blank);
208 
209 	sc->sc_accel = ((device_cfdata(&sc->sc_dv)->cf_flags &
210 	    FFB_CFFLAG_NOACCEL) == 0);
211 
212 	wsfont_init();
213 
214 	vcons_init(&sc->vd, sc, &ffb_stdscreen, &ffb_accessops);
215 	sc->vd.init_screen = ffb_init_screen;
216 
217 	/* we mess with ffb_console_screen only once */
218 	if (sc->sc_console) {
219 		vcons_init_screen(&sc->vd, &ffb_console_screen, 1, &defattr);
220 		SCREEN_VISIBLE((&ffb_console_screen));
221 		/*
222 		 * XXX we shouldn't use a global variable for the console
223 		 * screen
224 		 */
225 		sc->vd.active = &ffb_console_screen;
226 		ffb_console_screen.scr_flags = VCONS_SCREEN_IS_STATIC;
227 	} else {
228 		if (ffb_console_screen.scr_ri.ri_rows == 0) {
229 			/* do some minimal setup to avoid weirdnesses later */
230 			vcons_init_screen(&sc->vd, &ffb_console_screen, 1, &defattr);
231 		}
232 	}
233 	ri = &ffb_console_screen.scr_ri;
234 
235 	ffb_stdscreen.nrows = ri->ri_rows;
236 	ffb_stdscreen.ncols = ri->ri_cols;
237 	ffb_stdscreen.textops = &ri->ri_ops;
238 	ffb_stdscreen.capabilities = ri->ri_caps;
239 
240 	sc->sc_fb.fb_driver = &ffb_fbdriver;
241 	sc->sc_fb.fb_type.fb_cmsize = 0;
242 	sc->sc_fb.fb_type.fb_size = maxrow * sc->sc_linebytes;
243 	sc->sc_fb.fb_type.fb_type = FBTYPE_CREATOR;
244 	sc->sc_fb.fb_type.fb_width = sc->sc_width;
245 	sc->sc_fb.fb_type.fb_depth = sc->sc_depth;
246 	sc->sc_fb.fb_type.fb_height = sc->sc_height;
247 	sc->sc_fb.fb_device = &sc->sc_dv;
248 	fb_attach(&sc->sc_fb, sc->sc_console);
249 
250 	ffb_clearscreen(sc);
251 
252 	if (sc->sc_console) {
253 		wsdisplay_cnattach(&ffb_stdscreen, ri, 0, 0, defattr);
254 		vcons_replay_msgbuf(&ffb_console_screen);
255 	}
256 
257 	waa.console = sc->sc_console;
258 	waa.scrdata = &ffb_screenlist;
259 	waa.accessops = &ffb_accessops;
260 	waa.accesscookie = &sc->vd;
261 	config_found(&sc->sc_dv, &waa, wsemuldisplaydevprint);
262 }
263 
264 int
265 ffb_ioctl(void *v, void *vs, u_long cmd, void *data, int flags, struct lwp *l)
266 {
267 	struct vcons_data *vd = v;
268 	struct ffb_softc *sc = vd->cookie;
269 	struct wsdisplay_fbinfo *wdf;
270 	struct vcons_screen *ms = vd->active;
271 
272 #ifdef FFB_DEBUG
273 	printf("ffb_ioctl: %s cmd _IO%s%s('%c', %lu)\n",
274 	       device_xname(&sc->sc_dv),
275 	       (cmd & IOC_IN) ? "W" : "", (cmd & IOC_OUT) ? "R" : "",
276 	       (char)IOCGROUP(cmd), cmd & 0xff);
277 #endif
278 
279 	switch (cmd) {
280 	case FBIOGTYPE:
281 		*(struct fbtype *)data = sc->sc_fb.fb_type;
282 		break;
283 	case FBIOGATTR:
284 #define fba ((struct fbgattr *)data)
285 		fba->real_type = sc->sc_fb.fb_type.fb_type;
286 		fba->owner = 0; 	/* XXX ??? */
287 		fba->fbtype = sc->sc_fb.fb_type;
288 		fba->sattr.flags = 0;
289 		fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type;
290 		fba->sattr.dev_specific[0] = -1;
291 		fba->emu_types[0] = sc->sc_fb.fb_type.fb_type;
292 		fba->emu_types[1] = -1;
293 #undef fba
294 		break;
295 
296 	case FBIOGETCMAP:
297 	case FBIOPUTCMAP:
298 		return EIO;
299 
300 	case FBIOGVIDEO:
301 	case FBIOSVIDEO:
302 		return ffb_blank(sc, cmd == FBIOGVIDEO?
303 		    WSDISPLAYIO_GVIDEO : WSDISPLAYIO_SVIDEO,
304 		    (u_int *)data);
305 		break;
306 	case FBIOGCURSOR:
307 	case FBIOSCURSOR:
308 		/* the console driver is not using the hardware cursor */
309 		break;
310 	case FBIOGCURPOS:
311 		printf("%s: FBIOGCURPOS not implemented\n", device_xname(&sc->sc_dv));
312 		return EIO;
313 	case FBIOSCURPOS:
314 		printf("%s: FBIOSCURPOS not implemented\n", device_xname(&sc->sc_dv));
315 		return EIO;
316 	case FBIOGCURMAX:
317 		printf("%s: FBIOGCURMAX not implemented\n", device_xname(&sc->sc_dv));
318 		return EIO;
319 
320 	case WSDISPLAYIO_GTYPE:
321 		*(u_int *)data = WSDISPLAY_TYPE_SUNFFB;
322 		break;
323 	case WSDISPLAYIO_SMODE:
324 		{
325 			if (sc->sc_mode != *(u_int *)data) {
326 				sc->sc_mode = *(u_int *)data;
327 				if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
328 				    (sc->sc_locked == 0)) {
329 					ffb_ras_init(sc);
330 					vcons_redraw_screen(ms);
331 				}
332 			}
333 		}
334 		break;
335 	case WSDISPLAYIO_GINFO:
336 		wdf = (void *)data;
337 		wdf->height = sc->sc_height;
338 		wdf->width  = sc->sc_width;
339 		wdf->depth  = 32;
340 		wdf->cmsize = 256; /* XXX */
341 		break;
342 #ifdef WSDISPLAYIO_LINEBYTES
343 	case WSDISPLAYIO_LINEBYTES:
344 		*(u_int *)data = sc->sc_linebytes;
345 		break;
346 #endif
347 	case WSDISPLAYIO_GETCMAP:
348 		break;/* XXX */
349 
350 	case WSDISPLAYIO_PUTCMAP:
351 		break;/* XXX */
352 
353 	case WSDISPLAYIO_SVIDEO:
354 	case WSDISPLAYIO_GVIDEO:
355 		return(ffb_blank(sc, cmd, (u_int *)data));
356 		break;
357 	case WSDISPLAYIO_GCURPOS:
358 	case WSDISPLAYIO_SCURPOS:
359 	case WSDISPLAYIO_GCURMAX:
360 	case WSDISPLAYIO_GCURSOR:
361 	case WSDISPLAYIO_SCURSOR:
362 		return EIO; /* not supported yet */
363 	default:
364 		return EPASSTHROUGH;
365         }
366 
367 	return (0);
368 }
369 
370 /* blank/unblank the screen */
371 static int
372 ffb_blank(struct ffb_softc *sc, u_long cmd, u_int *data)
373 {
374 	struct vcons_screen *ms = sc->vd.active;
375 	u_int val;
376 
377 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_GSBLANK);
378 	val = DAC_READ(sc, FFB_DAC_VALUE);
379 
380 	switch (cmd) {
381 	case WSDISPLAYIO_GVIDEO:
382 		*data = val & 1;
383 		return(0);
384 		break;
385 	case WSDISPLAYIO_SVIDEO:
386 		if (*data == WSDISPLAYIO_VIDEO_OFF)
387 			val &= ~1;
388 		else if (*data == WSDISPLAYIO_VIDEO_ON)
389 			val |= 1;
390 		else
391 			return(EINVAL);
392 		break;
393 	default:
394 		return(EINVAL);
395 	}
396 
397 	DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_GSBLANK);
398 	DAC_WRITE(sc, FFB_DAC_VALUE, val);
399 
400 	if ((val & 1) && sc->sc_needredraw) {
401 		if (ms != NULL) {
402 			if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
403 			    (sc->sc_locked == 0)) {
404 				ffb_ras_init(sc);
405 				vcons_redraw_screen(ms);
406 			}
407 		}
408 	}
409 
410 	return(0);
411 }
412 
413 paddr_t
414 ffb_mmap(void *vsc, void *vs, off_t off, int prot)
415 {
416 	struct vcons_data *vd = vsc;
417 	struct ffb_softc *sc = vd->cookie;
418 	int i;
419 
420 	switch (sc->sc_mode) {
421 	case WSDISPLAYIO_MODE_MAPPED:
422 		for (i = 0; i < sc->sc_nreg; i++) {
423 			/* Before this set? */
424 			if (off < sc->sc_addrs[i])
425 				continue;
426 			/* After this set? */
427 			if (off >= (sc->sc_addrs[i] + sc->sc_sizes[i]))
428 				continue;
429 
430 			return (bus_space_mmap(sc->sc_bt, sc->sc_addrs[i],
431 			    off - sc->sc_addrs[i], prot, BUS_SPACE_MAP_LINEAR));
432 		}
433 		break;
434 #ifdef WSDISPLAYIO_MODE_DUMBFB
435 	case WSDISPLAYIO_MODE_DUMBFB:
436 		if (sc->sc_nreg < FFB_REG_DFB24)
437 			break;
438 		if (off >= 0 && off < sc->sc_sizes[FFB_REG_DFB24])
439 			return (bus_space_mmap(sc->sc_bt,
440 			    sc->sc_addrs[FFB_REG_DFB24], off, prot,
441 			    BUS_SPACE_MAP_LINEAR));
442 		break;
443 #endif
444 	}
445 	return (-1);
446 }
447 
448 void
449 ffb_ras_fifo_wait(struct ffb_softc *sc, int n)
450 {
451 	int32_t cache = sc->sc_fifo_cache;
452 
453 	if (cache < n) {
454 		do {
455 			cache = FBC_READ(sc, FFB_FBC_UCSR);
456 			cache = (cache & FBC_UCSR_FIFO_MASK) - 8;
457 		} while (cache < n);
458 	}
459 	sc->sc_fifo_cache = cache - n;
460 }
461 
462 void
463 ffb_ras_wait(struct ffb_softc *sc)
464 {
465 	uint32_t ucsr, r;
466 
467 	while (1) {
468 		ucsr = FBC_READ(sc, FFB_FBC_UCSR);
469 		if ((ucsr & (FBC_UCSR_FB_BUSY|FBC_UCSR_RP_BUSY)) == 0)
470 			break;
471 		r = ucsr & (FBC_UCSR_READ_ERR | FBC_UCSR_FIFO_OVFL);
472 		if (r != 0)
473 			FBC_WRITE(sc, FFB_FBC_UCSR, r);
474 	}
475 }
476 
477 void
478 ffb_ras_init(struct ffb_softc *sc)
479 {
480 	ffb_ras_fifo_wait(sc, 7);
481 	FBC_WRITE(sc, FFB_FBC_PPC,
482 	    FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
483 	    FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST);
484 	FBC_WRITE(sc, FFB_FBC_FBC,
485 	    FFB_FBC_WB_A | FFB_FBC_RB_A | FFB_FBC_SB_BOTH |
486 	    FFB_FBC_XE_OFF | FFB_FBC_RGBE_MASK);
487 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
488 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
489 	FBC_WRITE(sc, FFB_FBC_PMASK, 0xffffffff);
490 	FBC_WRITE(sc, FFB_FBC_FONTINC, 0x10000);
491 	sc->sc_fg_cache = 0;
492 	FBC_WRITE(sc, FFB_FBC_FG, sc->sc_fg_cache);
493 	ffb_ras_wait(sc);
494 }
495 
496 void
497 ffb_ras_eraserows(void *cookie, int row, int n, long attr)
498 {
499 	struct rasops_info *ri = cookie;
500 	struct vcons_screen *scr = ri->ri_hw;
501 	struct ffb_softc *sc = scr->scr_cookie;
502 
503 	if (row < 0) {
504 		n += row;
505 		row = 0;
506 	}
507 	if (row + n > ri->ri_rows)
508 		n = ri->ri_rows - row;
509 	if (n <= 0)
510 		return;
511 
512 	ffb_ras_fill(sc);
513 	ffb_ras_setfg(sc, ri->ri_devcmap[(attr >> 16) & 0xf]);
514 	ffb_ras_fifo_wait(sc, 4);
515 	if ((n == ri->ri_rows) && (ri->ri_flg & RI_FULLCLEAR)) {
516 		FBC_WRITE(sc, FFB_FBC_BY, 0);
517 		FBC_WRITE(sc, FFB_FBC_BX, 0);
518 		FBC_WRITE(sc, FFB_FBC_BH, ri->ri_height);
519 		FBC_WRITE(sc, FFB_FBC_BW, ri->ri_width);
520 	} else {
521 		row *= ri->ri_font->fontheight;
522 		FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + row);
523 		FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin);
524 		FBC_WRITE(sc, FFB_FBC_BH, n * ri->ri_font->fontheight);
525 		FBC_WRITE(sc, FFB_FBC_BW, ri->ri_emuwidth);
526 	}
527 	SYNC;
528 }
529 
530 void
531 ffb_ras_erasecols(void *cookie, int row, int col, int n, long attr)
532 {
533 	struct rasops_info *ri = cookie;
534 	struct vcons_screen *scr = ri->ri_hw;
535 	struct ffb_softc *sc = scr->scr_cookie;
536 
537 	if ((row < 0) || (row >= ri->ri_rows))
538 		return;
539 	if (col < 0) {
540 		n += col;
541 		col = 0;
542 	}
543 	if (col + n > ri->ri_cols)
544 		n = ri->ri_cols - col;
545 	if (n <= 0)
546 		return;
547 	n *= ri->ri_font->fontwidth;
548 	col *= ri->ri_font->fontwidth;
549 	row *= ri->ri_font->fontheight;
550 
551 	ffb_ras_fill(sc);
552 	ffb_ras_setfg(sc, ri->ri_devcmap[(attr >> 16) & 0xf]);
553 	ffb_ras_fifo_wait(sc, 4);
554 	FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + row);
555 	FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin + col);
556 	FBC_WRITE(sc, FFB_FBC_BH, ri->ri_font->fontheight);
557 	FBC_WRITE(sc, FFB_FBC_BW, n - 1);
558 	SYNC;
559 }
560 
561 void
562 ffb_ras_fill(struct ffb_softc *sc)
563 {
564 	ffb_ras_fifo_wait(sc, 2);
565 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
566 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
567 	SYNC;
568 }
569 
570 void
571 ffb_ras_copyrows(void *cookie, int src, int dst, int n)
572 {
573 	struct rasops_info *ri = cookie;
574 	struct vcons_screen *scr = ri->ri_hw;
575 	struct ffb_softc *sc = scr->scr_cookie;
576 
577 	if (dst == src)
578 		return;
579 	if (src < 0) {
580 		n += src;
581 		src = 0;
582 	}
583 	if ((src + n) > ri->ri_rows)
584 		n = ri->ri_rows - src;
585 	if (dst < 0) {
586 		n += dst;
587 		dst = 0;
588 	}
589 	if ((dst + n) > ri->ri_rows)
590 		n = ri->ri_rows - dst;
591 	if (n <= 0)
592 		return;
593 	n *= ri->ri_font->fontheight;
594 	src *= ri->ri_font->fontheight;
595 	dst *= ri->ri_font->fontheight;
596 
597 	ffb_ras_fifo_wait(sc, 8);
598 	FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_OLD | (FBC_ROP_OLD << 8));
599 	FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_VSCROLL);
600 	FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + src);
601 	FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin);
602 	FBC_WRITE(sc, FFB_FBC_DY, ri->ri_yorigin + dst);
603 	FBC_WRITE(sc, FFB_FBC_DX, ri->ri_xorigin);
604 	FBC_WRITE(sc, FFB_FBC_BH, n);
605 	FBC_WRITE(sc, FFB_FBC_BW, ri->ri_emuwidth);
606 	SYNC;
607 }
608 
609 static void
610 ffb_ras_setfg(struct ffb_softc *sc, int32_t fg)
611 {
612 	ffb_ras_fifo_wait(sc, 1);
613 	if (fg == sc->sc_fg_cache)
614 		return;
615 	sc->sc_fg_cache = fg;
616 	FBC_WRITE(sc, FFB_FBC_FG, fg);
617 	SYNC;
618 }
619 
620 static void
621 ffb_ras_setbg(struct ffb_softc *sc, int32_t bg)
622 {
623 	ffb_ras_fifo_wait(sc, 1);
624 	if (bg == sc->sc_bg_cache)
625 		return;
626 	sc->sc_bg_cache = bg;
627 	FBC_WRITE(sc, FFB_FBC_BG, bg);
628 	SYNC;
629 }
630 
631 /* frame buffer generic driver support functions */
632 static void
633 ffbfb_unblank(struct device *dev)
634 {
635 	struct ffb_softc *sc = device_private(dev);
636 	struct vcons_screen *ms = sc->vd.active;
637 	u_int on = 1;
638 	int redraw = 0;
639 
640 	ffb_ras_init(sc);
641 	if (sc->sc_locked) {
642 		sc->sc_locked = 0;
643 		redraw = 1;
644 	}
645 
646 	ffb_blank(sc, WSDISPLAYIO_SVIDEO, &on);
647 #if 0
648 	if ((sc->vd.active != &ffb_console_screen) &&
649 	    (ffb_console_screen.scr_flags & VCONS_SCREEN_IS_STATIC)) {
650 		/*
651 		 * force-switch to the console screen.
652 		 * Caveat: the higher layer will think we're still on the
653 		 * other screen
654 		 */
655 
656 		SCREEN_INVISIBLE(sc->vd.active);
657 		sc->vd.active = &ffb_console_screen;
658 		SCREEN_VISIBLE(sc->vd.active);
659 		ms = sc->vd.active;
660 		redraw = 1;
661 	}
662 #endif
663 	if (redraw) {
664 		vcons_redraw_screen(ms);
665 	}
666 }
667 
668 int
669 ffbfb_open(dev_t dev, int flags, int mode, struct lwp *l)
670 {
671 	struct ffb_softc *sc;
672 
673 	sc = device_lookup_private(&ffb_cd, minor(dev));
674 	if (sc == NULL)
675 		return ENXIO;
676 
677 	sc->sc_locked = 1;
678 	return 0;
679 }
680 
681 int
682 ffbfb_close(dev_t dev, int flags, int mode, struct lwp *l)
683 {
684 	struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
685 	struct vcons_screen *ms = sc->vd.active;
686 
687 	sc->sc_locked = 0;
688 	if (ms != NULL) {
689 		if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
690 		    (sc->sc_locked == 0)) {
691 			ffb_ras_init(sc);
692 			vcons_redraw_screen(ms);
693 		}
694 	}
695 	return 0;
696 }
697 
698 int
699 ffbfb_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
700 {
701 	struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
702 
703 	return ffb_ioctl(&sc->vd, NULL, cmd, data, flags, l);
704 }
705 
706 paddr_t
707 ffbfb_mmap(dev_t dev, off_t off, int prot)
708 {
709 	struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
710 	uint64_t size;
711 	int i, reg;
712 	off_t o;
713 
714 	/*
715 	 * off is a magic cookie (see xfree86/drivers/sunffb/ffb.h),
716 	 * which we map to an index into the "reg" property, and use
717 	 * our copy of the firmware data as arguments for the real
718 	 * mapping.
719 	 */
720 	static struct { unsigned long voff; int reg; } map[] = {
721 		{ 0x00000000, FFB_REG_SFB8R },
722 		{ 0x00400000, FFB_REG_SFB8G },
723 		{ 0x00800000, FFB_REG_SFB8B },
724 		{ 0x00c00000, FFB_REG_SFB8X },
725 		{ 0x01000000, FFB_REG_SFB32 },
726 		{ 0x02000000, FFB_REG_SFB64  },
727 		{ 0x04000000, FFB_REG_FBC },
728 		{ 0x04004000, FFB_REG_DFB8R },
729 		{ 0x04404000, FFB_REG_DFB8G },
730 		{ 0x04804000, FFB_REG_DFB8B },
731 		{ 0x04c04000, FFB_REG_DFB8X },
732 		{ 0x05004000, FFB_REG_DFB24 },
733 		{ 0x06004000, FFB_REG_DFB32 },
734 		{ 0x07004000, FFB_REG_DFB422A },
735 		{ 0x0bc06000, FFB_REG_DAC },
736 		{ 0x0bc08000, FFB_REG_PROM },
737 		{ 0x0bc18000, 0 }
738 	};
739 
740 	/* special value "FFB_EXP_VOFF" - not backed by any "reg" entry */
741 	if (off == 0x0bc18000)
742 		return bus_space_mmap(sc->sc_bt, sc->sc_addrs[FFB_REG_PROM],
743 		    0x00200000, prot, BUS_SPACE_MAP_LINEAR);
744 
745 	/*
746 	 * FFB_VOFF_FBC_KREGS - used by afbinit to upload firmware. We should
747 	 * probably mmap them only on afb boards
748 	 */
749 	if ((off >= 0x0bc04000) && (off < 0x0bc06000))
750 		return bus_space_mmap(sc->sc_bt, sc->sc_addrs[FFB_REG_PROM],
751 		    0x00610000 + (off - 0x0bc04000), prot,
752 		    BUS_SPACE_MAP_LINEAR);
753 
754 #define NELEMS(arr) (sizeof(arr)/sizeof((arr)[0]))
755 
756 	/* the map is ordered by voff */
757 	for (i = 0; i < NELEMS(map)-1; i++) {
758 		reg = map[i].reg;
759 		/* the number of entries in reg seems to vary */
760 		if (reg < sc->sc_nreg) {
761 			size = min((map[i + 1].voff - map[i].voff),
762 			    sc->sc_sizes[reg]);
763 			if ((off >= map[i].voff) &&
764 			    (off < (map[i].voff + size))) {
765 				o = off - map[i].voff;
766 				return bus_space_mmap(sc->sc_bt,
767 				    sc->sc_addrs[reg], o, prot,
768 				    BUS_SPACE_MAP_LINEAR);
769 			}
770 		}
771 	}
772 
773 	return -1;
774 }
775 
776 void
777 ffb_clearscreen(struct ffb_softc *sc)
778 {
779 	struct rasops_info *ri = &ffb_console_screen.scr_ri;
780 	ffb_ras_fill(sc);
781 	ffb_ras_setfg(sc, ri->ri_devcmap[WS_DEFAULT_BG]);
782 	ffb_ras_fifo_wait(sc, 4);
783 	FBC_WRITE(sc, FFB_FBC_BY, 0);
784 	FBC_WRITE(sc, FFB_FBC_BX, 0);
785 	FBC_WRITE(sc, FFB_FBC_BH, sc->sc_height);
786 	FBC_WRITE(sc, FFB_FBC_BW, sc->sc_width);
787 }
788 
789 void
790 ffb_cursor(void *cookie, int on, int row, int col)
791 {
792 	struct rasops_info *ri = cookie;
793 	struct vcons_screen *scr;
794 	struct ffb_softc *sc;
795 	int x, y, wi, he, coffset;
796 
797 	if (cookie != NULL) {
798 		scr = ri->ri_hw;
799 		sc = scr->scr_cookie;
800 
801 		wi = ri->ri_font->fontwidth;
802 		he = ri->ri_font->fontheight;
803 
804 		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
805 			x = ri->ri_ccol * wi + ri->ri_xorigin;
806 			y = ri->ri_crow * he + ri->ri_yorigin;
807 
808 			if (ri->ri_flg & RI_CURSOR) {
809 				/* remove cursor */
810 				coffset = ri->ri_ccol + (ri->ri_crow *
811 				    ri->ri_cols);
812 #ifdef WSDISPLAY_SCROLLSUPPORT
813 				coffset += scr->scr_offset_to_zero;
814 #endif
815 				ffb_ras_wait(sc);
816 				ffb_putchar(cookie, ri->ri_crow,
817 				    ri->ri_ccol, scr->scr_chars[coffset],
818 				    scr->scr_attrs[coffset]);
819 				ri->ri_flg &= ~RI_CURSOR;
820 			}
821 			ri->ri_crow = row;
822 			ri->ri_ccol = col;
823 			if (on)
824 			{
825 				long attr, revattr;
826 				x = ri->ri_ccol * wi + ri->ri_xorigin;
827 				y = ri->ri_crow * he + ri->ri_yorigin;
828 				coffset = col + (row * ri->ri_cols);
829 #ifdef WSDISPLAY_SCROLLSUPPORT
830 				coffset += scr->scr_offset_to_zero;
831 #endif
832 				attr = scr->scr_attrs[coffset];
833 #ifdef FFB_CURSOR_SWAP_COLOURS
834 				revattr=((attr >> 8 ) & 0x000f0000) | ((attr &
835 				    0x000f0000)<<8) | (attr & 0x0000ffff);
836 #else
837 				revattr = attr ^ 0xffff0000;
838 #endif
839 				ffb_ras_wait(sc);
840 				ffb_putchar(cookie, ri->ri_crow, ri->ri_ccol,
841 				    scr->scr_chars[coffset], revattr);
842 				ri->ri_flg |= RI_CURSOR;
843 			}
844 		} else {
845 			ri->ri_crow = row;
846 			ri->ri_ccol = col;
847 			ri->ri_flg &= ~RI_CURSOR;
848 		}
849 	}
850 }
851 
852 void
853 ffb_putchar(void *cookie, int row, int col, u_int c, long attr)
854 {
855 	struct rasops_info *ri = cookie;
856 	struct vcons_screen *scr = ri->ri_hw;
857 	struct wsdisplay_font *font = PICK_FONT(ri, c);
858 	struct ffb_softc *sc = scr->scr_cookie;
859 
860 	/*
861 	 * font operations don't use the blitter so we have to wait here
862 	 * in case we were scrolling
863 	 */
864 
865 	if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
866 		void *data;
867 		uint32_t fg, bg;
868 		int uc, i;
869 		int x, y, wi, he;
870 
871 		wi = font->fontwidth;
872 		he = font->fontheight;
873 
874 		if (!CHAR_IN_FONT(c, font))
875 			return;
876 		bg = ri->ri_devcmap[(attr >> 16) & 0xf];
877 		fg = ri->ri_devcmap[(attr >> 24) & 0xf];
878 		x = ri->ri_xorigin + col * wi;
879 		y = ri->ri_yorigin + row * he;
880 
881 		uc = c - font->firstchar;
882 		data = (uint8_t *)font->data + uc * ri->ri_fontscale;
883 
884 		ffb_ras_setbg(sc, bg);
885 		ffb_ras_setfg(sc, fg);
886 		ffb_ras_fifo_wait(sc, 3);
887 		FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
888 		FBC_WRITE(sc, FFB_FBC_FONTXY, (y << 16) | x);
889 		FBC_WRITE(sc, FFB_FBC_FONTW, wi);
890 
891 		switch (ri->ri_font->stride) {
892 			case 1: {
893 				uint8_t *data8 = data;
894 				uint32_t reg;
895 				for (i = 0; i < he; i++) {
896 					reg = *data8;
897 					FBC_WRITE(sc, FFB_FBC_FONT, reg << 24);
898 					data8++;
899 				}
900 				break;
901 			}
902 			case 2: {
903 				uint16_t *data16 = data;
904 				uint32_t reg;
905 				for (i = 0; i < he; i++) {
906 					reg = *data16;
907 					FBC_WRITE(sc, FFB_FBC_FONT, reg << 16);
908 					data16++;
909 				}
910 				break;
911 			}
912 		}
913 	}
914 }
915 
916 int
917 ffb_allocattr(void *cookie, int fg, int bg, int flags, long *attrp)
918 {
919 	if ((fg == 0) && (bg == 0))
920 	{
921 		fg = WS_DEFAULT_FG;
922 		bg = WS_DEFAULT_BG;
923 	}
924 	if (flags & WSATTR_REVERSE) {
925 		*attrp = (bg & 0xff) << 24 | (fg & 0xff) << 16 |
926 		    (flags & 0xff);
927 	} else
928 		*attrp = (fg & 0xff) << 24 | (bg & 0xff) << 16 |
929 		    (flags & 0xff);
930 	return 0;
931 }
932 
933 void
934 ffb_init_screen(void *cookie, struct vcons_screen *scr,
935     int existing, long *defattr)
936 {
937 	struct ffb_softc *sc = cookie;
938 	struct rasops_info *ri = &scr->scr_ri;
939 
940 	ri->ri_depth = 32;
941 	ri->ri_width = sc->sc_width;
942 	ri->ri_height = sc->sc_height;
943 	ri->ri_stride = sc->sc_linebytes;
944 	ri->ri_flg = RI_CENTER;
945 
946 	/*
947 	 * we can't accelerate copycols() so instead of falling back to
948 	 * software use vcons' putchar() based implementation
949 	 */
950 	scr->scr_flags |= VCONS_NO_COPYCOLS;
951 
952 #ifdef FFB_DEBUG
953 	printf("addr: %08lx\n",(ulong)ri->ri_bits);
954 #endif
955 	rasops_init(ri, sc->sc_height/8, sc->sc_width/8);
956 	ri->ri_caps = WSSCREEN_WSCOLORS;
957 	rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
958 		    sc->sc_width / ri->ri_font->fontwidth);
959 
960 	/* enable acceleration */
961 	ri->ri_ops.copyrows = ffb_ras_copyrows;
962 	ri->ri_ops.eraserows = ffb_ras_eraserows;
963 	ri->ri_ops.erasecols = ffb_ras_erasecols;
964 	ri->ri_ops.cursor = ffb_cursor;
965 	ri->ri_ops.allocattr = ffb_allocattr;
966 	ri->ri_ops.putchar = ffb_putchar;
967 }
968