xref: /netbsd-src/sys/dev/ic/vga.c (revision 4472dbe5e3bd91ef2540bada7a7ca7384627ff9b)
1 /* $NetBSD: vga.c,v 1.26 2000/03/23 07:01:33 thorpej Exp $ */
2 
3 /*
4  * Copyright (c) 1995, 1996 Carnegie-Mellon University.
5  * All rights reserved.
6  *
7  * Author: Chris G. Demetriou
8  *
9  * Permission to use, copy, modify and distribute this software and
10  * its documentation is hereby granted, provided that both the copyright
11  * notice and this permission notice appear in all copies of the
12  * software, derivative works or modified versions, and any portions
13  * thereof, and that both notices appear in supporting documentation.
14  *
15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
17  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18  *
19  * Carnegie Mellon requests users of this software to return to
20  *
21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
22  *  School of Computer Science
23  *  Carnegie Mellon University
24  *  Pittsburgh PA 15213-3890
25  *
26  * any improvements or extensions that they make and grant Carnegie the
27  * rights to redistribute these changes.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/callout.h>
33 #include <sys/kernel.h>
34 #include <sys/device.h>
35 #include <sys/malloc.h>
36 #include <sys/queue.h>
37 #include <machine/bus.h>
38 
39 #include <dev/isa/isavar.h>
40 #include <dev/isa/isareg.h>
41 
42 #include <dev/ic/mc6845reg.h>
43 #include <dev/ic/pcdisplayvar.h>
44 #include <dev/ic/vgareg.h>
45 #include <dev/ic/vgavar.h>
46 
47 #include <dev/wscons/wsdisplayvar.h>
48 #include <dev/wscons/wsconsio.h>
49 #include <dev/wscons/unicode.h>
50 
51 #include <dev/ic/pcdisplay.h>
52 
53 #include "opt_wsdisplay_compat.h" /* for WSCONS_SUPPORT_PCVTFONTS */
54 
55 static struct vgafont {
56 	char name[16];
57 	int height;
58 	int encoding;
59 #ifdef notyet
60 	int firstchar, numchars;
61 #endif
62 	int slot;
63 } vga_builtinfont = {
64 	"builtin",
65 	16,
66 	WSDISPLAY_FONTENC_IBM,
67 #ifdef notyet
68 	0, 256,
69 #endif
70 	0
71 };
72 
73 struct vgascreen {
74 	struct pcdisplayscreen pcs;
75 
76 	LIST_ENTRY(vgascreen) next;
77 
78 	struct vga_config *cfg;
79 
80 	/* videostate */
81 	struct vgafont *fontset1, *fontset2;
82 	/* font data */
83 	/* palette */
84 
85 	int mindispoffset, maxdispoffset;
86 };
87 
88 struct vga_config {
89 	struct vga_handle hdl;
90 
91 	int nscreens;
92 	LIST_HEAD(, vgascreen) screens;
93 	struct vgascreen *active; /* current display */
94 	const struct wsscreen_descr *currenttype;
95 	int currentfontset1, currentfontset2;
96 
97 	int vc_biosmapped;
98 	bus_space_tag_t vc_biostag;
99 	bus_space_handle_t vc_bioshdl;
100 
101 	struct vgafont *vc_fonts[8];
102 
103 	struct vgascreen *wantedscreen;
104 	void (*switchcb) __P((void *, int, int));
105 	void *switchcbarg;
106 
107 	struct callout vc_switch_callout;
108 };
109 
110 static int vgaconsole, vga_console_type, vga_console_attached;
111 static struct vgascreen vga_console_screen;
112 static struct vga_config vga_console_vc;
113 
114 int vga_selectfont __P((struct vga_config *, struct vgascreen *,
115 			char *, char *));
116 void vga_init_screen __P((struct vga_config *, struct vgascreen *,
117 			  const struct wsscreen_descr *,
118 			  int, long *));
119 void vga_init __P((struct vga_config *, bus_space_tag_t,
120 		   bus_space_tag_t));
121 static void vga_setfont __P((struct vga_config *, struct vgascreen *));
122 
123 static int vga_mapchar __P((void *, int, unsigned int *));
124 static int	vga_alloc_attr __P((void *, int, int, int, long *));
125 void	vga_copyrows __P((void *, int, int, int));
126 
127 const struct wsdisplay_emulops vga_emulops = {
128 	pcdisplay_cursor,
129 	vga_mapchar,
130 	pcdisplay_putchar,
131 	pcdisplay_copycols,
132 	pcdisplay_erasecols,
133 	vga_copyrows,
134 	pcdisplay_eraserows,
135 	vga_alloc_attr
136 };
137 
138 /*
139  * translate WS(=ANSI) color codes to standard pc ones
140  */
141 static unsigned char fgansitopc[] = {
142 #ifdef __alpha__
143 	/*
144 	 * XXX DEC HAS SWITCHED THE CODES FOR BLUE AND RED!!!
145 	 * XXX We should probably not bother with this
146 	 * XXX (reinitialize the palette registers).
147 	 */
148 	FG_BLACK, FG_BLUE, FG_GREEN, FG_CYAN, FG_RED,
149 	FG_MAGENTA, FG_BROWN, FG_LIGHTGREY
150 #else
151 	FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE,
152 	FG_MAGENTA, FG_CYAN, FG_LIGHTGREY
153 #endif
154 }, bgansitopc[] = {
155 #ifdef __alpha__
156 	BG_BLACK, BG_BLUE, BG_GREEN, BG_CYAN, BG_RED,
157 	BG_MAGENTA, BG_BROWN, BG_LIGHTGREY
158 #else
159 	BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE,
160 	BG_MAGENTA, BG_CYAN, BG_LIGHTGREY
161 #endif
162 };
163 
164 const struct wsscreen_descr vga_stdscreen = {
165 	"80x25", 80, 25,
166 	&vga_emulops,
167 	8, 16,
168 	WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_BLINK
169 }, vga_stdscreen_mono = {
170 	"80x25", 80, 25,
171 	&vga_emulops,
172 	8, 16,
173 	WSSCREEN_HILIT | WSSCREEN_UNDERLINE | WSSCREEN_BLINK | WSSCREEN_REVERSE
174 }, vga_stdscreen_bf = {
175 	"80x25bf", 80, 25,
176 	&vga_emulops,
177 	8, 16,
178 	WSSCREEN_WSCOLORS | WSSCREEN_BLINK
179 }, vga_40lscreen = {
180 	"80x40", 80, 40,
181 	&vga_emulops,
182 	8, 10,
183 	WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_BLINK
184 }, vga_40lscreen_mono = {
185 	"80x40", 80, 40,
186 	&vga_emulops,
187 	8, 10,
188 	WSSCREEN_HILIT | WSSCREEN_UNDERLINE | WSSCREEN_BLINK | WSSCREEN_REVERSE
189 }, vga_40lscreen_bf = {
190 	"80x40bf", 80, 40,
191 	&vga_emulops,
192 	8, 10,
193 	WSSCREEN_WSCOLORS | WSSCREEN_BLINK
194 }, vga_50lscreen = {
195 	"80x50", 80, 50,
196 	&vga_emulops,
197 	8, 8,
198 	WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_BLINK
199 }, vga_50lscreen_mono = {
200 	"80x50", 80, 50,
201 	&vga_emulops,
202 	8, 8,
203 	WSSCREEN_HILIT | WSSCREEN_UNDERLINE | WSSCREEN_BLINK | WSSCREEN_REVERSE
204 }, vga_50lscreen_bf = {
205 	"80x50bf", 80, 50,
206 	&vga_emulops,
207 	8, 8,
208 	WSSCREEN_WSCOLORS | WSSCREEN_BLINK
209 };
210 
211 #define VGA_SCREEN_CANTWOFONTS(type) (!((type)->capabilities & WSSCREEN_HILIT))
212 
213 const struct wsscreen_descr *_vga_scrlist[] = {
214 	&vga_stdscreen,
215 	&vga_stdscreen_bf,
216 	&vga_40lscreen,
217 	&vga_40lscreen_bf,
218 	&vga_50lscreen,
219 	&vga_50lscreen_bf,
220 	/* XXX other formats, graphics screen? */
221 }, *_vga_scrlist_mono[] = {
222 	&vga_stdscreen_mono,
223 	&vga_40lscreen_mono,
224 	&vga_50lscreen_mono,
225 	/* XXX other formats, graphics screen? */
226 };
227 
228 const struct wsscreen_list vga_screenlist = {
229 	sizeof(_vga_scrlist) / sizeof(struct wsscreen_descr *),
230 	_vga_scrlist
231 }, vga_screenlist_mono = {
232 	sizeof(_vga_scrlist_mono) / sizeof(struct wsscreen_descr *),
233 	_vga_scrlist_mono
234 };
235 
236 static int	vga_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
237 static int	vga_mmap __P((void *, off_t, int));
238 static int	vga_alloc_screen __P((void *, const struct wsscreen_descr *,
239 				      void **, int *, int *, long *));
240 static void	vga_free_screen __P((void *, void *));
241 static int	vga_show_screen __P((void *, void *, int,
242 				     void (*) (void *, int, int), void *));
243 static int	vga_load_font __P((void *, void *, struct wsdisplay_font *));
244 
245 void vga_doswitch __P((struct vga_config *));
246 
247 const struct wsdisplay_accessops vga_accessops = {
248 	vga_ioctl,
249 	vga_mmap,
250 	vga_alloc_screen,
251 	vga_free_screen,
252 	vga_show_screen,
253 	vga_load_font
254 };
255 
256 /*
257  * The following functions implement back-end configuration grabbing
258  * and attachment.
259  */
260 int
261 vga_common_probe(iot, memt)
262 	bus_space_tag_t iot, memt;
263 {
264 	bus_space_handle_t ioh_vga, ioh_6845, memh;
265 	u_int8_t regval;
266 	u_int16_t vgadata;
267 	int gotio_vga, gotio_6845, gotmem, mono, rv;
268 	int dispoffset;
269 
270 	gotio_vga = gotio_6845 = gotmem = rv = 0;
271 
272 	if (bus_space_map(iot, 0x3c0, 0x10, 0, &ioh_vga))
273 		goto bad;
274 	gotio_vga = 1;
275 
276 	/* read "misc output register" */
277 	regval = bus_space_read_1(iot, ioh_vga, 0xc);
278 	mono = !(regval & 1);
279 
280 	if (bus_space_map(iot, (mono ? 0x3b0 : 0x3d0), 0x10, 0, &ioh_6845))
281 		goto bad;
282 	gotio_6845 = 1;
283 
284 	if (bus_space_map(memt, 0xa0000, 0x20000, 0, &memh))
285 		goto bad;
286 	gotmem = 1;
287 
288 	dispoffset = (mono ? 0x10000 : 0x18000);
289 
290 	vgadata = bus_space_read_2(memt, memh, dispoffset);
291 	bus_space_write_2(memt, memh, dispoffset, 0xa55a);
292 	if (bus_space_read_2(memt, memh, dispoffset) != 0xa55a)
293 		goto bad;
294 	bus_space_write_2(memt, memh, dispoffset, vgadata);
295 
296 	/*
297 	 * check if this is really a VGA
298 	 * (try to write "Color Select" register as XFree86 does)
299 	 * XXX check before if at least EGA?
300 	 */
301 	/* reset state */
302 	(void) bus_space_read_1(iot, ioh_6845, 10);
303 	bus_space_write_1(iot, ioh_vga, VGA_ATC_INDEX,
304 			  20 | 0x20); /* colselect | enable */
305 	regval = bus_space_read_1(iot, ioh_vga, VGA_ATC_DATAR);
306 	/* toggle the implemented bits */
307 	bus_space_write_1(iot, ioh_vga, VGA_ATC_DATAW, regval ^ 0x0f);
308 	bus_space_write_1(iot, ioh_vga, VGA_ATC_INDEX,
309 			  20 | 0x20);
310 	/* read back */
311 	if (bus_space_read_1(iot, ioh_vga, VGA_ATC_DATAR) != (regval ^ 0x0f))
312 		goto bad;
313 	/* restore contents */
314 	bus_space_write_1(iot, ioh_vga, VGA_ATC_DATAW, regval);
315 
316 	rv = 1;
317 bad:
318 	if (gotio_vga)
319 		bus_space_unmap(iot, ioh_vga, 0x10);
320 	if (gotio_6845)
321 		bus_space_unmap(iot, ioh_6845, 0x10);
322 	if (gotmem)
323 		bus_space_unmap(memt, memh, 0x20000);
324 
325 	return (rv);
326 }
327 
328 /*
329  * We want at least ASCII 32..127 be present in the
330  * first font slot.
331  */
332 #define vga_valid_primary_font(f) \
333 	(f->encoding == WSDISPLAY_FONTENC_IBM || \
334 	f->encoding == WSDISPLAY_FONTENC_ISO)
335 
336 int
337 vga_selectfont(vc, scr, name1, name2)
338 	struct vga_config *vc;
339 	struct vgascreen *scr;
340 	char *name1, *name2; /* NULL: take first found */
341 {
342 	const struct wsscreen_descr *type = scr->pcs.type;
343 	struct vgafont *f1, *f2;
344 	int i;
345 
346 	f1 = f2 = 0;
347 
348 	for (i = 0; i < 8; i++) {
349 		struct vgafont *f = vc->vc_fonts[i];
350 		if (!f || f->height != type->fontheight)
351 			continue;
352 		if (!f1 &&
353 		    vga_valid_primary_font(f) &&
354 		    (!name1 || !strcmp(name1, f->name))) {
355 			f1 = f;
356 			continue;
357 		}
358 		if (!f2 &&
359 		    VGA_SCREEN_CANTWOFONTS(type) &&
360 		    (!name2 || !strcmp(name2, f->name))) {
361 			f2 = f;
362 			continue;
363 		}
364 	}
365 
366 	/*
367 	 * The request fails if no primary font was found,
368 	 * or if a second font was requested but not found.
369 	 */
370 	if (f1 && (!name2 || f2)) {
371 #ifdef VGAFONTDEBUG
372 		if (scr != &vga_console_screen || vga_console_attached) {
373 			printf("vga (%s): font1=%s (slot %d)", type->name,
374 			       f1->name, f1->slot);
375 			if (f2)
376 				printf(", font2=%s (slot %d)",
377 				       f2->name, f2->slot);
378 			printf("\n");
379 		}
380 #endif
381 		scr->fontset1 = f1;
382 		scr->fontset2 = f2;
383 		return (0);
384 	}
385 	return (ENXIO);
386 }
387 
388 void
389 vga_init_screen(vc, scr, type, existing, attrp)
390 	struct vga_config *vc;
391 	struct vgascreen *scr;
392 	const struct wsscreen_descr *type;
393 	int existing;
394 	long *attrp;
395 {
396 	int cpos;
397 	int res;
398 
399 	scr->cfg = vc;
400 	scr->pcs.hdl = (struct pcdisplay_handle *)&vc->hdl;
401 	scr->pcs.type = type;
402 	scr->pcs.active = 0;
403 	scr->mindispoffset = 0;
404 	scr->maxdispoffset = 0x8000 - type->nrows * type->ncols * 2;
405 
406 	if (existing) {
407 		cpos = vga_6845_read(&vc->hdl, cursorh) << 8;
408 		cpos |= vga_6845_read(&vc->hdl, cursorl);
409 
410 		/* make sure we have a valid cursor position */
411 		if (cpos < 0 || cpos >= type->nrows * type->ncols)
412 			cpos = 0;
413 
414 		scr->pcs.dispoffset = vga_6845_read(&vc->hdl, startadrh) << 9;
415 		scr->pcs.dispoffset |= vga_6845_read(&vc->hdl, startadrl) << 1;
416 
417 		/* make sure we have a valid memory offset */
418 		if (scr->pcs.dispoffset < scr->mindispoffset ||
419 		    scr->pcs.dispoffset > scr->maxdispoffset)
420 			scr->pcs.dispoffset = scr->mindispoffset;
421 	} else {
422 		cpos = 0;
423 		scr->pcs.dispoffset = scr->mindispoffset;
424 	}
425 
426 	scr->pcs.vc_crow = cpos / type->ncols;
427 	scr->pcs.vc_ccol = cpos % type->ncols;
428 	pcdisplay_cursor_init(&scr->pcs, existing);
429 
430 #ifdef __alpha__
431 	if (!vc->hdl.vh_mono)
432 		/*
433 		 * DEC firmware uses a blue background.
434 		 */
435 		res = vga_alloc_attr(scr, WSCOL_WHITE, WSCOL_BLUE,
436 				     WSATTR_WSCOLORS, attrp);
437 	else
438 #endif
439 	res = vga_alloc_attr(scr, 0, 0, 0, attrp);
440 #ifdef DIAGNOSTIC
441 	if (res)
442 		panic("vga_init_screen: attribute botch");
443 #endif
444 
445 	scr->pcs.mem = NULL;
446 
447 	scr->fontset1 = scr->fontset2 = 0;
448 	if (vga_selectfont(vc, scr, 0, 0)) {
449 		if (scr == &vga_console_screen)
450 			panic("vga_init_screen: no font");
451 		else
452 			printf("vga_init_screen: no font\n");
453 	}
454 
455 	vc->nscreens++;
456 	LIST_INSERT_HEAD(&vc->screens, scr, next);
457 }
458 
459 void
460 vga_init(vc, iot, memt)
461 	struct vga_config *vc;
462 	bus_space_tag_t iot, memt;
463 {
464 	struct vga_handle *vh = &vc->hdl;
465 	u_int8_t mor;
466 	int i;
467 
468         vh->vh_iot = iot;
469         vh->vh_memt = memt;
470 
471         if (bus_space_map(vh->vh_iot, 0x3c0, 0x10, 0, &vh->vh_ioh_vga))
472                 panic("vga_common_setup: couldn't map vga io");
473 
474 	/* read "misc output register" */
475 	mor = bus_space_read_1(vh->vh_iot, vh->vh_ioh_vga, 0xc);
476 	vh->vh_mono = !(mor & 1);
477 
478 	if (bus_space_map(vh->vh_iot, (vh->vh_mono ? 0x3b0 : 0x3d0), 0x10, 0,
479 			  &vh->vh_ioh_6845))
480                 panic("vga_common_setup: couldn't map 6845 io");
481 
482         if (bus_space_map(vh->vh_memt, 0xa0000, 0x20000, 0, &vh->vh_allmemh))
483                 panic("vga_common_setup: couldn't map memory");
484 
485         if (bus_space_subregion(vh->vh_memt, vh->vh_allmemh,
486 				(vh->vh_mono ? 0x10000 : 0x18000), 0x8000,
487 				&vh->vh_memh))
488                 panic("vga_common_setup: mem subrange failed");
489 
490 	/* should only reserve the space (no need to map - save KVM) */
491 	vc->vc_biostag = memt;
492 	if (bus_space_map(vc->vc_biostag, 0xc0000, 0x8000, 0,
493 			  &vc->vc_bioshdl))
494 		vc->vc_biosmapped = 0;
495 	else
496 		vc->vc_biosmapped = 1;
497 
498 	vc->nscreens = 0;
499 	LIST_INIT(&vc->screens);
500 	vc->active = NULL;
501 	vc->currenttype = vh->vh_mono ? &vga_stdscreen_mono : &vga_stdscreen;
502 	callout_init(&vc->vc_switch_callout);
503 
504 	vc->vc_fonts[0] = &vga_builtinfont;
505 	for (i = 1; i < 8; i++)
506 		vc->vc_fonts[i] = 0;
507 
508 	vc->currentfontset1 = vc->currentfontset2 = 0;
509 }
510 
511 void
512 vga_common_attach(self, iot, memt, type)
513 	struct device *self;
514 	bus_space_tag_t iot, memt;
515 	int type;
516 {
517 	int console;
518 	struct vga_config *vc;
519 	struct wsemuldisplaydev_attach_args aa;
520 
521 	console = vga_is_console(iot, type);
522 
523 	if (console) {
524 		vc = &vga_console_vc;
525 		vga_console_attached = 1;
526 	} else {
527 		vc = malloc(sizeof(struct vga_config), M_DEVBUF, M_WAITOK);
528 		vga_init(vc, iot, memt);
529 	}
530 
531 	aa.console = console;
532 	aa.scrdata = (vc->hdl.vh_mono ? &vga_screenlist_mono : &vga_screenlist);
533 	aa.accessops = &vga_accessops;
534 	aa.accesscookie = vc;
535 
536         config_found(self, &aa, wsemuldisplaydevprint);
537 }
538 
539 int
540 vga_cnattach(iot, memt, type, check)
541 	bus_space_tag_t iot, memt;
542 	int type, check;
543 {
544 	long defattr;
545 	const struct wsscreen_descr *scr;
546 
547 	if (check && !vga_common_probe(iot, memt))
548 		return (ENXIO);
549 
550 	/* set up bus-independent VGA configuration */
551 	vga_init(&vga_console_vc, iot, memt);
552 	scr = vga_console_vc.currenttype;
553 	vga_init_screen(&vga_console_vc, &vga_console_screen, scr, 1, &defattr);
554 
555 	vga_console_screen.pcs.active = 1;
556 	vga_console_vc.active = &vga_console_screen;
557 
558 	wsdisplay_cnattach(scr, &vga_console_screen,
559 			   vga_console_screen.pcs.vc_ccol,
560 			   vga_console_screen.pcs.vc_crow,
561 			   defattr);
562 
563 	vgaconsole = 1;
564 	vga_console_type = type;
565 	return (0);
566 }
567 
568 int
569 vga_is_console(iot, type)
570 	bus_space_tag_t iot;
571 	int type;
572 {
573 	if (vgaconsole &&
574 	    !vga_console_attached &&
575 	    iot == vga_console_vc.hdl.vh_iot &&
576 	    (vga_console_type == -1 || (type == vga_console_type)))
577 		return (1);
578 	return (0);
579 }
580 
581 int
582 vga_ioctl(v, cmd, data, flag, p)
583 	void *v;
584 	u_long cmd;
585 	caddr_t data;
586 	int flag;
587 	struct proc *p;
588 {
589 #if 0
590 	struct vga_config *vc = v;
591 #endif
592 
593 	switch (cmd) {
594 #if 0
595 	case WSDISPLAYIO_GTYPE:
596 		*(int *)data = vc->vc_type;
597 		/* XXX should get detailed hardware information here */
598 		return 0;
599 #else
600 	case WSDISPLAYIO_GTYPE:
601 		*(int *)data = WSDISPLAY_TYPE_UNKNOWN;
602 		return 0;
603 #endif
604 
605 	case WSDISPLAYIO_GINFO:
606 	case WSDISPLAYIO_GETCMAP:
607 	case WSDISPLAYIO_PUTCMAP:
608 	case WSDISPLAYIO_GVIDEO:
609 	case WSDISPLAYIO_SVIDEO:
610 	case WSDISPLAYIO_GCURPOS:
611 	case WSDISPLAYIO_SCURPOS:
612 	case WSDISPLAYIO_GCURMAX:
613 	case WSDISPLAYIO_GCURSOR:
614 	case WSDISPLAYIO_SCURSOR:
615 		/* NONE of these operations are by the generic VGA driver. */
616 		return ENOTTY;
617 	}
618 
619 	return -1;
620 }
621 
622 static int
623 vga_mmap(v, offset, prot)
624 	void *v;
625 	off_t offset;
626 	int prot;
627 {
628 
629 	/* XXX */
630 	return -1;
631 }
632 
633 int
634 vga_alloc_screen(v, type, cookiep, curxp, curyp, defattrp)
635 	void *v;
636 	const struct wsscreen_descr *type;
637 	void **cookiep;
638 	int *curxp, *curyp;
639 	long *defattrp;
640 {
641 	struct vga_config *vc = v;
642 	struct vgascreen *scr;
643 
644 	if (vc->nscreens == 1) {
645 		/*
646 		 * When allocating the second screen, get backing store
647 		 * for the first one too.
648 		 * XXX We could be more clever and use video RAM.
649 		 */
650 		vc->screens.lh_first->pcs.mem =
651 		  malloc(type->ncols * type->nrows * 2, M_DEVBUF, M_WAITOK);
652 	}
653 
654 	scr = malloc(sizeof(struct vgascreen), M_DEVBUF, M_WAITOK);
655 	vga_init_screen(vc, scr, type, vc->nscreens == 0, defattrp);
656 
657 	if (vc->nscreens == 1) {
658 		scr->pcs.active = 1;
659 		vc->active = scr;
660 		vc->currenttype = type;
661 	} else {
662 		scr->pcs.mem = malloc(type->ncols * type->nrows * 2,
663 				      M_DEVBUF, M_WAITOK);
664 		pcdisplay_eraserows(&scr->pcs, 0, type->nrows, *defattrp);
665 	}
666 
667 	*cookiep = scr;
668 	*curxp = scr->pcs.vc_ccol;
669 	*curyp = scr->pcs.vc_crow;
670 
671 	return (0);
672 }
673 
674 void
675 vga_free_screen(v, cookie)
676 	void *v;
677 	void *cookie;
678 {
679 	struct vgascreen *vs = cookie;
680 	struct vga_config *vc = vs->cfg;
681 
682 	LIST_REMOVE(vs, next);
683 	if (vs != &vga_console_screen)
684 		free(vs, M_DEVBUF);
685 	else
686 		panic("vga_free_screen: console");
687 
688 	if (vc->active == vs)
689 		vc->active = 0;
690 }
691 
692 static void
693 vga_setfont(vc, scr)
694 	struct vga_config *vc;
695 	struct vgascreen *scr;
696 {
697 	int fontslot1, fontslot2;
698 
699 	fontslot1 = (scr->fontset1 ? scr->fontset1->slot : 0);
700 	fontslot2 = (scr->fontset2 ? scr->fontset2->slot : fontslot1);
701 	if (vc->currentfontset1 != fontslot1 ||
702 	    vc->currentfontset2 != fontslot2) {
703 		vga_setfontset(&vc->hdl, fontslot1, fontslot2);
704 		vc->currentfontset1 = fontslot1;
705 		vc->currentfontset2 = fontslot2;
706 	}
707 }
708 
709 int
710 vga_show_screen(v, cookie, waitok, cb, cbarg)
711 	void *v;
712 	void *cookie;
713 	int waitok;
714 	void (*cb) __P((void *, int, int));
715 	void *cbarg;
716 {
717 	struct vgascreen *scr = cookie, *oldscr;
718 	struct vga_config *vc = scr->cfg;
719 
720 	oldscr = vc->active; /* can be NULL! */
721 	if (scr == oldscr) {
722 		return (0);
723 	}
724 
725 	vc->wantedscreen = cookie;
726 	vc->switchcb = cb;
727 	vc->switchcbarg = cbarg;
728 	if (cb) {
729 		callout_reset(&vc->vc_switch_callout, 0,
730 		    (void(*)(void *))vga_doswitch, vc);
731 		return (EAGAIN);
732 	}
733 
734 	vga_doswitch(vc);
735 	return (0);
736 }
737 
738 void
739 vga_doswitch(vc)
740 	struct vga_config *vc;
741 {
742 	struct vgascreen *scr, *oldscr;
743 	struct vga_handle *vh = &vc->hdl;
744 	const struct wsscreen_descr *type;
745 
746 	scr = vc->wantedscreen;
747 	if (!scr) {
748 		printf("vga_doswitch: disappeared\n");
749 		(*vc->switchcb)(vc->switchcbarg, EIO, 0);
750 		return;
751 	}
752 	type = scr->pcs.type;
753 	oldscr = vc->active; /* can be NULL! */
754 #ifdef DIAGNOSTIC
755 	if (oldscr) {
756 		if (!oldscr->pcs.active)
757 			panic("vga_show_screen: not active");
758 		if (oldscr->pcs.type != vc->currenttype)
759 			panic("vga_show_screen: bad type");
760 	}
761 #endif
762 	if (scr == oldscr) {
763 		return;
764 	}
765 #ifdef DIAGNOSTIC
766 	if (scr->pcs.active)
767 		panic("vga_show_screen: active");
768 #endif
769 
770 	if (oldscr) {
771 		const struct wsscreen_descr *oldtype = oldscr->pcs.type;
772 
773 		oldscr->pcs.active = 0;
774 		bus_space_read_region_2(vh->vh_memt, vh->vh_memh,
775 					oldscr->pcs.dispoffset, oldscr->pcs.mem,
776 					oldtype->ncols * oldtype->nrows);
777 	}
778 
779 	if (vc->currenttype != type) {
780 		vga_setscreentype(vh, type);
781 		vc->currenttype = type;
782 	}
783 
784 	vga_setfont(vc, scr);
785 	/* XXX swich colours! */
786 
787 	scr->pcs.dispoffset = scr->mindispoffset;
788 	if (!oldscr || (scr->pcs.dispoffset != oldscr->pcs.dispoffset)) {
789 		vga_6845_write(vh, startadrh, scr->pcs.dispoffset >> 9);
790 		vga_6845_write(vh, startadrl, scr->pcs.dispoffset >> 1);
791 	}
792 
793 	bus_space_write_region_2(vh->vh_memt, vh->vh_memh,
794 				scr->pcs.dispoffset, scr->pcs.mem,
795 				type->ncols * type->nrows);
796 	scr->pcs.active = 1;
797 
798 	vc->active = scr;
799 
800 	pcdisplay_cursor(&scr->pcs, scr->pcs.cursoron,
801 			 scr->pcs.vc_crow, scr->pcs.vc_ccol);
802 
803 	vc->wantedscreen = 0;
804 	if (vc->switchcb)
805 		(*vc->switchcb)(vc->switchcbarg, 0, 0);
806 }
807 
808 static int
809 vga_load_font(v, cookie, data)
810 	void *v;
811 	void *cookie;
812 	struct wsdisplay_font *data;
813 {
814 	struct vga_config *vc = v;
815 	struct vgascreen *scr = cookie;
816 	char *name2;
817 	int res, slot;
818 	struct vgafont *f;
819 
820 	if (scr) {
821 		name2 = strchr(data->name, ',');
822 		if (name2)
823 			*name2++ = '\0';
824 		res = vga_selectfont(vc, scr, data->name, name2);
825 		if (!res)
826 			vga_setfont(vc, scr);
827 		return (res);
828 	}
829 
830 	if (data->fontwidth != 8 || data->stride != 1)
831 		return (EINVAL); /* XXX 1 byte per line */
832 	if (data->firstchar != 0 || data->numchars != 256)
833 		return (EINVAL);
834 #ifndef WSCONS_SUPPORT_PCVTFONTS
835 	if (data->encoding == WSDISPLAY_FONTENC_PCVT) {
836 		printf("vga: pcvt font support not built in, see vga(4)\n");
837 		return (EINVAL);
838 	}
839 #endif
840 
841 	for (slot = 0; slot < 8; slot++)
842 		if (!vc->vc_fonts[slot])
843 			break;
844 	if (slot == 8)
845 		return (ENOSPC);
846 
847 	f = malloc(sizeof(struct vgafont), M_DEVBUF, M_WAITOK);
848 	strncpy(f->name, data->name, sizeof(f->name));
849 	f->height = data->fontheight;
850 	f->encoding = data->encoding;
851 #ifdef notyet
852 	f->firstchar = data->firstchar;
853 	f->numchars = data->numchars;
854 #endif
855 #ifdef VGAFONTDEBUG
856 	printf("vga: load %s (8x%d, enc %d) font to slot %d\n", f->name,
857 	       f->height, f->encoding, slot);
858 #endif
859 	vga_loadchars(&vc->hdl, slot, 0, 256, f->height, data->data);
860 	f->slot = slot;
861 	vc->vc_fonts[slot] = f;
862 
863 	return (0);
864 }
865 
866 static int
867 vga_alloc_attr(id, fg, bg, flags, attrp)
868 	void *id;
869 	int fg, bg;
870 	int flags;
871 	long *attrp;
872 {
873 	struct vgascreen *scr = id;
874 	struct vga_config *vc = scr->cfg;
875 
876 	if (vc->hdl.vh_mono) {
877 		if (flags & WSATTR_WSCOLORS)
878 			return (EINVAL);
879 		if (flags & WSATTR_REVERSE)
880 			*attrp = 0x70;
881 		else
882 			*attrp = 0x07;
883 		if (flags & WSATTR_UNDERLINE)
884 			*attrp |= FG_UNDERLINE;
885 		if (flags & WSATTR_HILIT)
886 			*attrp |= FG_INTENSE;
887 	} else {
888 		if (flags & (WSATTR_UNDERLINE | WSATTR_REVERSE))
889 			return (EINVAL);
890 		if (flags & WSATTR_WSCOLORS)
891 			*attrp = fgansitopc[fg] | bgansitopc[bg];
892 		else
893 			*attrp = 7;
894 		if (flags & WSATTR_HILIT)
895 			*attrp += 8;
896 	}
897 	if (flags & WSATTR_BLINK)
898 		*attrp |= FG_BLINK;
899 	return (0);
900 }
901 
902 void
903 vga_copyrows(id, srcrow, dstrow, nrows)
904 	void *id;
905 	int srcrow, dstrow, nrows;
906 {
907 	struct vgascreen *scr = id;
908 	bus_space_tag_t memt = scr->pcs.hdl->ph_memt;
909 	bus_space_handle_t memh = scr->pcs.hdl->ph_memh;
910 	int ncols = scr->pcs.type->ncols;
911 	bus_size_t srcoff, dstoff;
912 
913 	srcoff = srcrow * ncols + 0;
914 	dstoff = dstrow * ncols + 0;
915 
916 	if (scr->pcs.active) {
917 		if (dstrow == 0 && (srcrow + nrows == scr->pcs.type->nrows)) {
918 #ifdef PCDISPLAY_SOFTCURSOR
919 			int cursoron = scr->pcs.cursoron;
920 
921 			if (cursoron)
922 				pcdisplay_cursor(&scr->pcs, 0,
923 				    scr->pcs.vc_crow, scr->pcs.vc_ccol);
924 #endif
925 			/* scroll up whole screen */
926 			if ((scr->pcs.dispoffset + srcrow * ncols * 2)
927 			    <= scr->maxdispoffset) {
928 				scr->pcs.dispoffset += srcrow * ncols * 2;
929 			} else {
930 				bus_space_copy_region_2(memt, memh,
931 					scr->pcs.dispoffset + srcoff * 2,
932 					memh, scr->mindispoffset,
933 					nrows * ncols);
934 				scr->pcs.dispoffset = scr->mindispoffset;
935 			}
936 			vga_6845_write(&scr->cfg->hdl, startadrh,
937 				       scr->pcs.dispoffset >> 9);
938 			vga_6845_write(&scr->cfg->hdl, startadrl,
939 				       scr->pcs.dispoffset >> 1);
940 #ifdef PCDISPLAY_SOFTCURSOR
941 			if (cursoron)
942 				pcdisplay_cursor(&scr->pcs, 1,
943 				    scr->pcs.vc_crow, scr->pcs.vc_ccol);
944 #endif
945 		} else {
946 			bus_space_copy_region_2(memt, memh,
947 					scr->pcs.dispoffset + srcoff * 2,
948 					memh, scr->pcs.dispoffset + dstoff * 2,
949 					nrows * ncols);
950 		}
951 	} else
952 		bcopy(&scr->pcs.mem[srcoff], &scr->pcs.mem[dstoff],
953 		      nrows * ncols * 2);
954 }
955 
956 #ifdef WSCONS_SUPPORT_PCVTFONTS
957 
958 #define NOTYET 0xffff
959 static u_int16_t pcvt_unichars[0xa0] = {
960 /* 0 */	_e006U,
961 	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
962 	NOTYET,
963 	0x2409, /* SYMBOL FOR HORIZONTAL TABULATION */
964 	0x240a, /* SYMBOL FOR LINE FEED */
965 	0x240b, /* SYMBOL FOR VERTICAL TABULATION */
966 	0x240c, /* SYMBOL FOR FORM FEED */
967 	0x240d, /* SYMBOL FOR CARRIAGE RETURN */
968 	NOTYET, NOTYET,
969 /* 1 */	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
970 	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
971 /* 2 */	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
972 	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
973 /* 3 */	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
974 	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
975 /* 4 */	0x03c1, /* GREEK SMALL LETTER RHO */
976 	0x03c8, /* GREEK SMALL LETTER PSI */
977 	0x2202, /* PARTIAL DIFFERENTIAL */
978 	0x03bb, /* GREEK SMALL LETTER LAMDA */
979 	0x03b9, /* GREEK SMALL LETTER IOTA */
980 	0x03b7, /* GREEK SMALL LETTER ETA */
981 	0x03b5, /* GREEK SMALL LETTER EPSILON */
982 	0x03c7, /* GREEK SMALL LETTER CHI */
983 	0x2228, /* LOGICAL OR */
984 	0x2227, /* LOGICAL AND */
985 	0x222a, /* UNION */
986 	0x2283, /* SUPERSET OF */
987 	0x2282, /* SUBSET OF */
988 	0x03a5, /* GREEK CAPITAL LETTER UPSILON */
989 	0x039e, /* GREEK CAPITAL LETTER XI */
990 	0x03a8, /* GREEK CAPITAL LETTER PSI */
991 /* 5 */	0x03a0, /* GREEK CAPITAL LETTER PI */
992 	0x21d2, /* RIGHTWARDS DOUBLE ARROW */
993 	0x21d4, /* LEFT RIGHT DOUBLE ARROW */
994 	0x039b, /* GREEK CAPITAL LETTER LAMDA */
995 	0x0398, /* GREEK CAPITAL LETTER THETA */
996 	0x2243, /* ASYMPTOTICALLY EQUAL TO */
997 	0x2207, /* NABLA */
998 	0x2206, /* INCREMENT */
999 	0x221d, /* PROPORTIONAL TO */
1000 	0x2234, /* THEREFORE */
1001 	0x222b, /* INTEGRAL */
1002 	0x2215, /* DIVISION SLASH */
1003 	0x2216, /* SET MINUS */
1004 	_e00eU,
1005 	_e00dU,
1006 	_e00bU,
1007 /* 6 */	_e00cU,
1008 	_e007U,
1009 	_e008U,
1010 	_e009U,
1011 	_e00aU,
1012 	0x221a, /* SQUARE ROOT */
1013 	0x03c9, /* GREEK SMALL LETTER OMEGA */
1014 	0x00a5, /* YEN SIGN */
1015 	0x03be, /* GREEK SMALL LETTER XI */
1016 	0x00fd, /* LATIN SMALL LETTER Y WITH ACUTE */
1017 	0x00fe, /* LATIN SMALL LETTER THORN */
1018 	0x00f0, /* LATIN SMALL LETTER ETH */
1019 	0x00de, /* LATIN CAPITAL LETTER THORN */
1020 	0x00dd, /* LATIN CAPITAL LETTER Y WITH ACUTE */
1021 	0x00d7, /* MULTIPLICATION SIGN */
1022 	0x00d0, /* LATIN CAPITAL LETTER ETH */
1023 /* 7 */	0x00be, /* VULGAR FRACTION THREE QUARTERS */
1024 	0x00b8, /* CEDILLA */
1025 	0x00b4, /* ACUTE ACCENT */
1026 	0x00af, /* MACRON */
1027 	0x00ae, /* REGISTERED SIGN */
1028 	0x00ad, /* SOFT HYPHEN */
1029 	0x00ac, /* NOT SIGN */
1030 	0x00a8, /* DIAERESIS */
1031 	0x2260, /* NOT EQUAL TO */
1032 	_e005U,
1033 	_e004U,
1034 	_e003U,
1035 	_e002U,
1036 	_e001U,
1037 	0x03c5, /* GREEK SMALL LETTER UPSILON */
1038 	0x00f8, /* LATIN SMALL LETTER O WITH STROKE */
1039 /* 8 */	0x0153, /* LATIN SMALL LIGATURE OE */
1040 	0x00f5, /* LATIN SMALL LETTER O WITH TILDE !!!doc bug */
1041 	0x00e3, /* LATIN SMALL LETTER A WITH TILDE */
1042 	0x0178, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */
1043 	0x00db, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
1044 	0x00da, /* LATIN CAPITAL LETTER U WITH ACUTE */
1045 	0x00d9, /* LATIN CAPITAL LETTER U WITH GRAVE */
1046 	0x00d8, /* LATIN CAPITAL LETTER O WITH STROKE */
1047 	0x0152, /* LATIN CAPITAL LIGATURE OE */
1048 	0x00d5, /* LATIN CAPITAL LETTER O WITH TILDE */
1049 	0x00d4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
1050 	0x00d3, /* LATIN CAPITAL LETTER O WITH ACUTE */
1051 	0x00d2, /* LATIN CAPITAL LETTER O WITH GRAVE */
1052 	0x00cf, /* LATIN CAPITAL LETTER I WITH DIAERESIS */
1053 	0x00ce, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
1054 	0x00cd, /* LATIN CAPITAL LETTER I WITH ACUTE */
1055 /* 9 */	0x00cc, /* LATIN CAPITAL LETTER I WITH GRAVE */
1056 	0x00cb, /* LATIN CAPITAL LETTER E WITH DIAERESIS */
1057 	0x00ca, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
1058 	0x00c8, /* LATIN CAPITAL LETTER E WITH GRAVE */
1059 	0x00c3, /* LATIN CAPITAL LETTER A WITH TILDE */
1060 	0x00c2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
1061 	0x00c1, /* LATIN CAPITAL LETTER A WITH ACUTE */
1062 	0x00c0, /* LATIN CAPITAL LETTER A WITH GRAVE */
1063 	0x00b9, /* SUPERSCRIPT ONE */
1064 	0x00b7, /* MIDDLE DOT */
1065 	0x03b6, /* GREEK SMALL LETTER ZETA */
1066 	0x00b3, /* SUPERSCRIPT THREE */
1067 	0x00a9, /* COPYRIGHT SIGN */
1068 	0x00a4, /* CURRENCY SIGN */
1069 	0x03ba, /* GREEK SMALL LETTER KAPPA */
1070 	_e000U
1071 };
1072 
1073 static int vga_pcvt_mapchar __P((int, unsigned int *));
1074 
1075 static int
1076 vga_pcvt_mapchar(uni, index)
1077 	int uni;
1078 	unsigned int *index;
1079 {
1080 	int i;
1081 
1082 	for (i = 0; i < 0xa0; i++) /* 0xa0..0xff are reserved */
1083 		if (uni == pcvt_unichars[i]) {
1084 			*index = i;
1085 			return (5);
1086 		}
1087 	*index = 0x99; /* middle dot */
1088 	return (0);
1089 }
1090 
1091 #endif /* WSCONS_SUPPORT_PCVTFONTS */
1092 
1093 static int _vga_mapchar __P((void *, struct vgafont *, int, unsigned int *));
1094 
1095 static int
1096 _vga_mapchar(id, font, uni, index)
1097 	void *id;
1098 	struct vgafont *font;
1099 	int uni;
1100 	unsigned int *index;
1101 {
1102 
1103 	switch (font->encoding) {
1104 	case WSDISPLAY_FONTENC_ISO:
1105 		if (uni < 256) {
1106 			*index = uni;
1107 			return (5);
1108 		} else {
1109 			*index = ' ';
1110 			return (0);
1111 		}
1112 		break;
1113 	case WSDISPLAY_FONTENC_IBM:
1114 		return (pcdisplay_mapchar(id, uni, index));
1115 #ifdef WSCONS_SUPPORT_PCVTFONTS
1116 	case WSDISPLAY_FONTENC_PCVT:
1117 		return (vga_pcvt_mapchar(uni, index));
1118 #endif
1119 	default:
1120 #ifdef VGAFONTDEBUG
1121 		printf("_vga_mapchar: encoding=%d\n", font->encoding);
1122 #endif
1123 		*index = ' ';
1124 		return (0);
1125 	}
1126 }
1127 
1128 static int
1129 vga_mapchar(id, uni, index)
1130 	void *id;
1131 	int uni;
1132 	unsigned int *index;
1133 {
1134 	struct vgascreen *scr = id;
1135 	unsigned int idx1, idx2;
1136 	int res1, res2;
1137 
1138 	res1 = 0;
1139 	idx1 = ' '; /* space */
1140 	if (scr->fontset1)
1141 		res1 = _vga_mapchar(id, scr->fontset1, uni, &idx1);
1142 	res2 = -1;
1143 	if (scr->fontset2) {
1144 		KASSERT(VGA_SCREEN_CANTWOFONTS(scr->pcs.type));
1145 		res2 = _vga_mapchar(id, scr->fontset2, uni, &idx2);
1146 	}
1147 	if (res2 > res1) {
1148 		*index = idx2 | 0x0800; /* attribute bit 3 */
1149 		return (res2);
1150 	}
1151 	*index = idx1;
1152 	return (res1);
1153 }
1154