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