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