xref: /netbsd-src/sys/dev/ic/vga.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /* $NetBSD: vga.c,v 1.111 2013/11/04 16:54:56 christos 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/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: vga.c,v 1.111 2013/11/04 16:54:56 christos Exp $");
32 
33 /* for WSCONS_SUPPORT_PCVTFONTS */
34 #include "opt_wsdisplay_compat.h"
35 /* for WSDISPLAY_CUSTOM_BORDER */
36 #include "opt_wsdisplay_border.h"
37 /* for WSDISPLAY_CUSTOM_OUTPUT */
38 #include "opt_wsmsgattrs.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/callout.h>
43 #include <sys/kernel.h>
44 #include <sys/device.h>
45 #include <sys/malloc.h>
46 #include <sys/queue.h>
47 #include <sys/bus.h>
48 
49 #include <dev/ic/mc6845reg.h>
50 #include <dev/ic/pcdisplayvar.h>
51 #include <dev/ic/vgareg.h>
52 #include <dev/ic/vgavar.h>
53 
54 #include <dev/wscons/wsdisplayvar.h>
55 #include <dev/wscons/wsconsio.h>
56 #include <dev/wscons/unicode.h>
57 #include <dev/wsfont/wsfont.h>
58 
59 #include <dev/ic/pcdisplay.h>
60 
61 int vga_no_builtinfont = 0;
62 
63 static struct wsdisplay_font _vga_builtinfont = {
64 	"builtin",			/* typeface name */
65 	0,				/* firstchar */
66 	256,				/* numbers */
67 	WSDISPLAY_FONTENC_IBM,		/* encoding */
68 	8,				/* width */
69 	16,				/* height */
70 	1,				/* stride */
71 	WSDISPLAY_FONTORDER_L2R,	/* bit order */
72 	0,				/* byte order */
73 	NULL				/* data */
74 };
75 
76 struct egavga_font {
77 	struct wsdisplay_font *wsfont;
78 	int cookie; /* wsfont handle, -1 invalid */
79 	int slot; /* in adapter RAM */
80 	int usecount;
81 	TAILQ_ENTRY(egavga_font) next; /* LRU queue */
82 };
83 
84 static struct egavga_font vga_builtinfont = {
85 	.wsfont = &_vga_builtinfont,
86 	.cookie = -1,
87 	.slot = 0,
88 };
89 
90 #ifdef VGA_CONSOLE_SCREENTYPE
91 static struct egavga_font vga_consolefont;
92 #endif
93 
94 struct vgascreen {
95 	struct pcdisplayscreen pcs;
96 
97 	LIST_ENTRY(vgascreen) next;
98 
99 	struct vga_config *cfg;
100 
101 	/* videostate */
102 	struct egavga_font *fontset1, *fontset2;
103 	/* font data */
104 
105 	int mindispoffset, maxdispoffset;
106 	int vga_rollover;
107 	int visibleoffset;
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 static struct egavga_font *egavga_getfont(struct vga_config *, struct vgascreen *,
115 				   const char *, int);
116 static void egavga_unreffont(struct vga_config *, struct egavga_font *);
117 
118 static int vga_selectfont(struct vga_config *, struct vgascreen *, const char *,
119 				   const char *);
120 static void vga_init_screen(struct vga_config *, struct vgascreen *,
121 		     const struct wsscreen_descr *, int, long *);
122 static void vga_init(struct vga_config *, bus_space_tag_t, bus_space_tag_t);
123 static void vga_setfont(struct vga_config *, struct vgascreen *);
124 
125 static int vga_mapchar(void *, int, unsigned int *);
126 static void vga_putchar(void *, int, int, u_int, long);
127 static int vga_allocattr(void *, int, int, int, long *);
128 static void vga_copyrows(void *, int, int, int);
129 #ifdef WSDISPLAY_SCROLLSUPPORT
130 static void vga_scroll (void *, void *, int);
131 #endif
132 
133 const struct wsdisplay_emulops vga_emulops = {
134 	pcdisplay_cursor,
135 	vga_mapchar,
136 	vga_putchar,
137 	pcdisplay_copycols,
138 	pcdisplay_erasecols,
139 	vga_copyrows,
140 	pcdisplay_eraserows,
141 	vga_allocattr,
142 #ifdef WSDISPLAY_CUSTOM_OUTPUT
143 	pcdisplay_replaceattr,
144 #else
145 	NULL,
146 #endif
147 };
148 
149 /*
150  * translate WS(=ANSI) color codes to standard pc ones
151  */
152 static const unsigned char fgansitopc[] = {
153 #ifdef __alpha__
154 	/*
155 	 * XXX DEC HAS SWITCHED THE CODES FOR BLUE AND RED!!!
156 	 * XXX We should probably not bother with this
157 	 * XXX (reinitialize the palette registers).
158 	 */
159 	FG_BLACK, FG_BLUE, FG_GREEN, FG_CYAN, FG_RED,
160 	FG_MAGENTA, FG_BROWN, FG_LIGHTGREY
161 #else
162 	FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE,
163 	FG_MAGENTA, FG_CYAN, FG_LIGHTGREY
164 #endif
165 }, bgansitopc[] = {
166 #ifdef __alpha__
167 	BG_BLACK, BG_BLUE, BG_GREEN, BG_CYAN, BG_RED,
168 	BG_MAGENTA, BG_BROWN, BG_LIGHTGREY
169 #else
170 	BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE,
171 	BG_MAGENTA, BG_CYAN, BG_LIGHTGREY
172 #endif
173 };
174 
175 const struct wsscreen_descr vga_25lscreen = {
176 	"80x25", 80, 25,
177 	&vga_emulops,
178 	8, 16,
179 	WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_BLINK,
180 	NULL,
181 }, vga_25lscreen_mono = {
182 	"80x25", 80, 25,
183 	&vga_emulops,
184 	8, 16,
185 	WSSCREEN_HILIT | WSSCREEN_UNDERLINE | WSSCREEN_BLINK | WSSCREEN_REVERSE,
186 	NULL,
187 }, vga_25lscreen_bf = {
188 	"80x25bf", 80, 25,
189 	&vga_emulops,
190 	8, 16,
191 	WSSCREEN_WSCOLORS | WSSCREEN_BLINK,
192 	NULL,
193 }, vga_40lscreen = {
194 	"80x40", 80, 40,
195 	&vga_emulops,
196 	8, 10,
197 	WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_BLINK,
198 	NULL,
199 }, vga_40lscreen_mono = {
200 	"80x40", 80, 40,
201 	&vga_emulops,
202 	8, 10,
203 	WSSCREEN_HILIT | WSSCREEN_UNDERLINE | WSSCREEN_BLINK | WSSCREEN_REVERSE,
204 	NULL,
205 }, vga_40lscreen_bf = {
206 	"80x40bf", 80, 40,
207 	&vga_emulops,
208 	8, 10,
209 	WSSCREEN_WSCOLORS | WSSCREEN_BLINK,
210 	NULL,
211 }, vga_50lscreen = {
212 	"80x50", 80, 50,
213 	&vga_emulops,
214 	8, 8,
215 	WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_BLINK,
216 	NULL,
217 }, vga_50lscreen_mono = {
218 	"80x50", 80, 50,
219 	&vga_emulops,
220 	8, 8,
221 	WSSCREEN_HILIT | WSSCREEN_UNDERLINE | WSSCREEN_BLINK | WSSCREEN_REVERSE,
222 	NULL,
223 }, vga_50lscreen_bf = {
224 	"80x50bf", 80, 50,
225 	&vga_emulops,
226 	8, 8,
227 	WSSCREEN_WSCOLORS | WSSCREEN_BLINK,
228 	NULL,
229 }, vga_24lscreen = {
230 	"80x24", 80, 24,
231 	&vga_emulops,
232 	8, 16,
233 	WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_BLINK,
234 	NULL,
235 }, vga_24lscreen_mono = {
236 	"80x24", 80, 24,
237 	&vga_emulops,
238 	8, 16,
239 	WSSCREEN_HILIT | WSSCREEN_UNDERLINE | WSSCREEN_BLINK | WSSCREEN_REVERSE,
240 	NULL,
241 }, vga_24lscreen_bf = {
242 	"80x24bf", 80, 24,
243 	&vga_emulops,
244 	8, 16,
245 	WSSCREEN_WSCOLORS | WSSCREEN_BLINK,
246 	NULL,
247 };
248 
249 #define VGA_SCREEN_CANTWOFONTS(type) (!((type)->capabilities & WSSCREEN_HILIT))
250 
251 const struct wsscreen_descr *_vga_scrlist[] = {
252 	&vga_25lscreen,
253 	&vga_25lscreen_bf,
254 	&vga_40lscreen,
255 	&vga_40lscreen_bf,
256 	&vga_50lscreen,
257 	&vga_50lscreen_bf,
258 	&vga_24lscreen,
259 	&vga_24lscreen_bf,
260 	/* XXX other formats, graphics screen? */
261 }, *_vga_scrlist_mono[] = {
262 	&vga_25lscreen_mono,
263 	&vga_40lscreen_mono,
264 	&vga_50lscreen_mono,
265 	&vga_24lscreen_mono,
266 	/* XXX other formats, graphics screen? */
267 };
268 
269 const struct wsscreen_list vga_screenlist = {
270 	sizeof(_vga_scrlist) / sizeof(struct wsscreen_descr *),
271 	_vga_scrlist
272 }, vga_screenlist_mono = {
273 	sizeof(_vga_scrlist_mono) / sizeof(struct wsscreen_descr *),
274 	_vga_scrlist_mono
275 };
276 
277 static int	vga_ioctl(void *, void *, u_long, void *, int, struct lwp *);
278 static paddr_t	vga_mmap(void *, void *, off_t, int);
279 static int	vga_alloc_screen(void *, const struct wsscreen_descr *,
280 				 void **, int *, int *, long *);
281 static void	vga_free_screen(void *, void *);
282 static int	vga_show_screen(void *, void *, int,
283 				void (*)(void *, int, int), void *);
284 static int	vga_load_font(void *, void *, struct wsdisplay_font *);
285 #ifdef WSDISPLAY_CUSTOM_BORDER
286 static int	vga_getborder(struct vga_config *, u_int *);
287 static int	vga_setborder(struct vga_config *, u_int);
288 #endif /* WSDISPLAY_CUSTOM_BORDER */
289 
290 static void vga_doswitch(struct vga_config *);
291 static void    vga_save_palette(struct vga_config *);
292 static void    vga_restore_palette(struct vga_config *);
293 
294 
295 const struct wsdisplay_accessops vga_accessops = {
296 	vga_ioctl,
297 	vga_mmap,
298 	vga_alloc_screen,
299 	vga_free_screen,
300 	vga_show_screen,
301 	vga_load_font,
302 	NULL,
303 #ifdef WSDISPLAY_SCROLLSUPPORT
304 	vga_scroll,
305 #else
306 	NULL,
307 #endif
308 };
309 
310 /*
311  * We want at least ASCII 32..127 be present in the
312  * first font slot.
313  */
314 #define vga_valid_primary_font(f) \
315 	(f->wsfont->encoding == WSDISPLAY_FONTENC_IBM || \
316 	f->wsfont->encoding == WSDISPLAY_FONTENC_ISO || \
317 	f->wsfont->encoding == WSDISPLAY_FONTENC_ISO2 || \
318 	f->wsfont->encoding == WSDISPLAY_FONTENC_ISO7 || \
319 	f->wsfont->encoding == WSDISPLAY_FONTENC_KOI8_R)
320 
321 static struct egavga_font *
322 egavga_getfont(struct vga_config *vc, struct vgascreen *scr, const char *name,
323 	       int primary)
324 {
325 	struct egavga_font *f;
326 	int cookie;
327 	struct wsdisplay_font *wf;
328 
329 	TAILQ_FOREACH(f, &vc->vc_fontlist, next) {
330 		if (wsfont_matches(f->wsfont, name,
331 		    8, scr->pcs.type->fontheight, 0, WSFONT_FIND_BITMAP) &&
332 		    (!primary || vga_valid_primary_font(f))) {
333 #ifdef VGAFONTDEBUG
334 			if (scr != &vga_console_screen || vga_console_attached)
335 				printf("vga_getfont: %s already present\n",
336 				    name ? name : "<default>");
337 #endif
338 			goto found;
339 		}
340 	}
341 
342 	cookie = wsfont_find(name, 8, scr->pcs.type->fontheight, 0,
343 	    WSDISPLAY_FONTORDER_L2R, 0, WSFONT_FIND_BITMAP);
344 	/* XXX obey "primary" */
345 	if (cookie == -1) {
346 #ifdef VGAFONTDEBUG
347 		if (scr != &vga_console_screen || vga_console_attached)
348 			printf("vga_getfont: %s not found\n",
349 			    name ? name : "<default>");
350 #endif
351 		return (0);
352 	}
353 
354 	if (wsfont_lock(cookie, &wf))
355 		return (0);
356 
357 #ifdef VGA_CONSOLE_SCREENTYPE
358 	if (scr == &vga_console_screen)
359 		f = &vga_consolefont;
360 	else
361 #endif
362 	f = malloc(sizeof(struct egavga_font), M_DEVBUF, M_NOWAIT);
363 	if (!f) {
364 		wsfont_unlock(cookie);
365 		return (0);
366 	}
367 	f->wsfont = wf;
368 	f->cookie = cookie;
369 	f->slot = -1; /* not yet loaded */
370 	f->usecount = 0; /* incremented below */
371 	TAILQ_INSERT_TAIL(&vc->vc_fontlist, f, next);
372 
373 found:
374 	f->usecount++;
375 #ifdef VGAFONTDEBUG
376 	if (scr != &vga_console_screen || vga_console_attached)
377 		printf("vga_getfont: usecount=%d\n", f->usecount);
378 #endif
379 	return (f);
380 }
381 
382 static void
383 egavga_unreffont(struct vga_config *vc, struct egavga_font *f)
384 {
385 
386 	f->usecount--;
387 #ifdef VGAFONTDEBUG
388 	printf("vga_unreffont: usecount=%d\n", f->usecount);
389 #endif
390 	if (f->usecount == 0 && f->cookie != -1) {
391 		TAILQ_REMOVE(&vc->vc_fontlist, f, next);
392 		if (f->slot != -1) {
393 			KASSERT(vc->vc_fonts[f->slot] == f);
394 			vc->vc_fonts[f->slot] = 0;
395 		}
396 		wsfont_unlock(f->cookie);
397 #ifdef VGA_CONSOLE_SCREENTYPE
398 		if (f != &vga_consolefont)
399 #endif
400 		free(f, M_DEVBUF);
401 	}
402 }
403 
404 static int
405 vga_selectfont(struct vga_config *vc, struct vgascreen *scr, const char *name1,
406 	       const char *name2)
407 {
408 	const struct wsscreen_descr *type = scr->pcs.type;
409 	struct egavga_font *f1, *f2;
410 
411 	f1 = egavga_getfont(vc, scr, name1, 1);
412 	if (!f1)
413 		return (ENXIO);
414 
415 	if (VGA_SCREEN_CANTWOFONTS(type) && name2) {
416 		f2 = egavga_getfont(vc, scr, name2, 0);
417 		if (!f2) {
418 			egavga_unreffont(vc, f1);
419 			return (ENXIO);
420 		}
421 	} else
422 		f2 = 0;
423 
424 #ifdef VGAFONTDEBUG
425 	if (scr != &vga_console_screen || vga_console_attached) {
426 		printf("vga (%s): font1=%s (slot %d)", type->name,
427 		    f1->wsfont->name, f1->slot);
428 		if (f2)
429 			printf(", font2=%s (slot %d)",
430 			    f2->wsfont->name, f2->slot);
431 		printf("\n");
432 	}
433 #endif
434 	if (scr->fontset1)
435 		egavga_unreffont(vc, scr->fontset1);
436 	scr->fontset1 = f1;
437 	if (scr->fontset2)
438 		egavga_unreffont(vc, scr->fontset2);
439 	scr->fontset2 = f2;
440 	return (0);
441 }
442 
443 static void
444 vga_init_screen(struct vga_config *vc, struct vgascreen *scr,
445 		const struct wsscreen_descr *type, int existing, long *attrp)
446 {
447 	int cpos;
448 	int res __diagused;
449 
450 	scr->cfg = vc;
451 	scr->pcs.hdl = (struct pcdisplay_handle *)&vc->hdl;
452 	scr->pcs.type = type;
453 	scr->pcs.active = existing;
454 	scr->mindispoffset = 0;
455 	if (vc->vc_quirks & VGA_QUIRK_NOFASTSCROLL)
456 		scr->maxdispoffset = 0;
457 	else
458 		scr->maxdispoffset = 0x8000 - type->nrows * type->ncols * 2;
459 
460 	if (existing) {
461 		vc->active = scr;
462 
463 		cpos = vga_6845_read(&vc->hdl, cursorh) << 8;
464 		cpos |= vga_6845_read(&vc->hdl, cursorl);
465 
466 		/* make sure we have a valid cursor position */
467 		if (cpos < 0 || cpos >= type->nrows * type->ncols)
468 			cpos = 0;
469 
470 		scr->pcs.dispoffset = vga_6845_read(&vc->hdl, startadrh) << 9;
471 		scr->pcs.dispoffset |= vga_6845_read(&vc->hdl, startadrl) << 1;
472 
473 		/* make sure we have a valid memory offset */
474 		if (scr->pcs.dispoffset < scr->mindispoffset ||
475 		    scr->pcs.dispoffset > scr->maxdispoffset)
476 			scr->pcs.dispoffset = scr->mindispoffset;
477 
478 		if (type != vc->currenttype) {
479 			vga_setscreentype(&vc->hdl, type);
480 			vc->currenttype = type;
481 		}
482 	} else {
483 		cpos = 0;
484 		scr->pcs.dispoffset = scr->mindispoffset;
485 	}
486 
487 	scr->pcs.visibleoffset = scr->pcs.dispoffset;
488 	scr->vga_rollover = 0;
489 
490 	scr->pcs.cursorrow = cpos / type->ncols;
491 	scr->pcs.cursorcol = cpos % type->ncols;
492 	pcdisplay_cursor_init(&scr->pcs, existing);
493 
494 #ifdef __alpha__
495 	if (!vc->hdl.vh_mono)
496 		/*
497 		 * DEC firmware uses a blue background.
498 		 * XXX These should be specified as kernel options for
499 		 * XXX alpha only, not hardcoded here (which is wrong
500 		 * XXX anyway because the emulation layer will assume
501 		 * XXX the default attribute is white on black).
502 		 */
503 		res = vga_allocattr(scr, WSCOL_WHITE, WSCOL_BLUE,
504 		    WSATTR_WSCOLORS, attrp);
505 	else
506 #endif
507 	res = vga_allocattr(scr, 0, 0, 0, attrp);
508 #ifdef DIAGNOSTIC
509 	if (res)
510 		panic("vga_init_screen: attribute botch");
511 #endif
512 
513 	scr->pcs.mem = NULL;
514 
515 	scr->fontset1 = scr->fontset2 = 0;
516 	if (vga_selectfont(vc, scr, 0, 0)) {
517 		if (scr == &vga_console_screen)
518 			panic("vga_init_screen: no font");
519 		else
520 			printf("vga_init_screen: no font\n");
521 	}
522 	if (existing)
523 		vga_setfont(vc, scr);
524 
525 	vc->nscreens++;
526 	LIST_INSERT_HEAD(&vc->screens, scr, next);
527 }
528 
529 static void
530 vga_init(struct vga_config *vc, bus_space_tag_t iot, bus_space_tag_t memt)
531 {
532 	struct vga_handle *vh = &vc->hdl;
533 	uint8_t mor;
534 	int i;
535 
536 	vh->vh_iot = iot;
537 	vh->vh_memt = memt;
538 
539 	if (bus_space_map(vh->vh_iot, 0x3c0, 0x10, 0, &vh->vh_ioh_vga))
540 		panic("vga_init: couldn't map vga io");
541 
542 	/* read "misc output register" */
543 	mor = vga_raw_read(vh, VGA_MISC_DATAR);
544 	vh->vh_mono = !(mor & 1);
545 
546 	if (bus_space_map(vh->vh_iot, (vh->vh_mono ? 0x3b0 : 0x3d0), 0x10, 0,
547 	    &vh->vh_ioh_6845))
548 		panic("vga_init: couldn't map 6845 io");
549 
550 	if (bus_space_map(vh->vh_memt, 0xa0000, 0x20000, 0, &vh->vh_allmemh))
551 		panic("vga_init: couldn't map memory");
552 
553 	if (bus_space_subregion(vh->vh_memt, vh->vh_allmemh,
554 	    (vh->vh_mono ? 0x10000 : 0x18000), 0x8000, &vh->vh_memh))
555 		panic("vga_init: mem subrange failed");
556 
557 	/* should only reserve the space (no need to map - save KVM) */
558 	vc->vc_biostag = memt;
559 	if (bus_space_map(vc->vc_biostag, 0xc0000, 0x8000, 0, &vc->vc_bioshdl))
560 		vc->vc_biosmapped = 0;
561 	else
562 		vc->vc_biosmapped = 1;
563 
564 	vc->nscreens = 0;
565 	LIST_INIT(&vc->screens);
566 	vc->active = NULL;
567 	vc->currenttype = vh->vh_mono ? &vga_25lscreen_mono : &vga_25lscreen;
568 	callout_init(&vc->vc_switch_callout, 0);
569 
570 	wsfont_init();
571 	if (vga_no_builtinfont) {
572 		struct wsdisplay_font *wf;
573 		int cookie;
574 
575 		cookie = wsfont_find(NULL, 8, 16, 0,
576 		     WSDISPLAY_FONTORDER_L2R, 0, WSFONT_FIND_BITMAP);
577 		if (cookie == -1 || wsfont_lock(cookie, &wf))
578 			panic("vga_init: can't load console font");
579 		vga_loadchars(&vc->hdl, 0, wf->firstchar, wf->numchars,
580 		    wf->fontheight, wf->data);
581 		vga_builtinfont.wsfont = wf;
582 		vga_builtinfont.cookie = cookie;
583 		vga_builtinfont.slot = 0;
584 	}
585 	vc->vc_fonts[0] = &vga_builtinfont;
586 	for (i = 1; i < 8; i++)
587 		vc->vc_fonts[i] = 0;
588 	TAILQ_INIT(&vc->vc_fontlist);
589 	TAILQ_INSERT_HEAD(&vc->vc_fontlist, &vga_builtinfont, next);
590 
591 	vc->currentfontset1 = vc->currentfontset2 = 0;
592 
593 	if (!vh->vh_mono && (u_int)WSDISPLAY_BORDER_COLOR < sizeof(fgansitopc))
594 		_vga_attr_write(vh, VGA_ATC_OVERSCAN,
595 		                fgansitopc[WSDISPLAY_BORDER_COLOR]);
596 	vga_save_palette(vc);
597 }
598 
599 void
600 vga_common_attach(struct vga_softc *sc, bus_space_tag_t iot,
601 		  bus_space_tag_t memt, int type, int quirks,
602 		  const struct vga_funcs *vf)
603 {
604 	int console;
605 	struct vga_config *vc;
606 	struct wsemuldisplaydev_attach_args aa;
607 
608 	console = vga_is_console(iot, type);
609 
610 	if (console) {
611 		vc = &vga_console_vc;
612 		vga_console_attached = 1;
613 	} else {
614 		vc = malloc(sizeof(struct vga_config), M_DEVBUF, M_WAITOK);
615 		vga_init(vc, iot, memt);
616 	}
617 
618 	if (quirks & VGA_QUIRK_ONEFONT) {
619 		vc->vc_nfontslots = 1;
620 #ifndef VGA_CONSOLE_ATI_BROKEN_FONTSEL
621 		/*
622 		 * XXX maybe invalidate font in slot > 0, but this can
623 		 * only be happen with VGA_CONSOLE_SCREENTYPE, and then
624 		 * we require VGA_CONSOLE_ATI_BROKEN_FONTSEL anyway.
625 		 */
626 #endif
627 	} else {
628 		vc->vc_nfontslots = 8;
629 #ifndef VGA_CONSOLE_ATI_BROKEN_FONTSEL
630 		/*
631 		 * XXX maybe validate builtin font shifted to slot 1 if
632 		 * slot 0 got overwritten because of VGA_CONSOLE_SCREENTYPE,
633 		 * but it will be reloaded anyway if needed.
634 		 */
635 #endif
636 	}
637 
638 	/*
639 	 * Save the builtin font to memory. In case it got overwritten
640 	 * in console initialization, use the copy in slot 1.
641 	 */
642 #ifdef VGA_CONSOLE_ATI_BROKEN_FONTSEL
643 #define BUILTINFONTLOC (vga_builtinfont.slot == -1 ? 1 : 0)
644 #else
645 	KASSERT(vga_builtinfont.slot == 0);
646 #define BUILTINFONTLOC (0)
647 #endif
648 	if (!vga_no_builtinfont) {
649 		char *data =
650 		    malloc(256 * vga_builtinfont.wsfont->fontheight,
651 		    M_DEVBUF, M_WAITOK);
652 		vga_readoutchars(&vc->hdl, BUILTINFONTLOC, 0, 256,
653 		    vga_builtinfont.wsfont->fontheight, data);
654 		vga_builtinfont.wsfont->data = data;
655 	}
656 
657 	vc->vc_type = type;
658 	vc->vc_funcs = vf;
659 	vc->vc_quirks = quirks;
660 
661 	sc->sc_vc = vc;
662 	vc->softc = sc;
663 
664 	aa.console = console;
665 	aa.scrdata = (vc->hdl.vh_mono ? &vga_screenlist_mono : &vga_screenlist);
666 	aa.accessops = &vga_accessops;
667 	aa.accesscookie = vc;
668 
669 	config_found_ia(sc->sc_dev, "wsemuldisplaydev", &aa, wsemuldisplaydevprint);
670 }
671 
672 int
673 vga_cnattach(bus_space_tag_t iot, bus_space_tag_t memt, int type, int check)
674 {
675 	long defattr;
676 	const struct wsscreen_descr *scr;
677 
678 	if (check && !vga_common_probe(iot, memt))
679 		return (ENXIO);
680 
681 	/* set up bus-independent VGA configuration */
682 	vga_init(&vga_console_vc, iot, memt);
683 #ifdef VGA_CONSOLE_SCREENTYPE
684 	scr = wsdisplay_screentype_pick(vga_console_vc.hdl.vh_mono ?
685 	    &vga_screenlist_mono : &vga_screenlist, VGA_CONSOLE_SCREENTYPE);
686 	if (!scr)
687 		panic("vga_cnattach: invalid screen type");
688 #else
689 	scr = vga_console_vc.currenttype;
690 #endif
691 #ifdef VGA_CONSOLE_ATI_BROKEN_FONTSEL
692 	/*
693 	 * On some (most/all?) ATI cards, only font slot 0 is usable.
694 	 * vga_init_screen() might need font slot 0 for a non-default
695 	 * console font, so save the builtin VGA font to another font slot.
696 	 * The attach() code will take care later.
697 	 */
698 	vga_console_vc.vc_quirks |= VGA_QUIRK_ONEFONT; /* redundant */
699 	vga_copyfont01(&vga_console_vc.hdl);
700 	vga_console_vc.vc_nfontslots = 1;
701 #else
702 	vga_console_vc.vc_nfontslots = 8;
703 #endif
704 #ifdef notdef
705 	/* until we know better, assume "fast scrolling" does not work */
706 	vga_console_vc.vc_quirks |= VGA_QUIRK_NOFASTSCROLL;
707 #endif
708 
709 	vga_init_screen(&vga_console_vc, &vga_console_screen, scr, 1, &defattr);
710 
711 	wsdisplay_cnattach(scr, &vga_console_screen,
712 	    vga_console_screen.pcs.cursorcol,
713 	    vga_console_screen.pcs.cursorrow, defattr);
714 
715 	vgaconsole = 1;
716 	vga_console_type = type;
717 	return (0);
718 }
719 
720 int
721 vga_cndetach(void)
722 {
723 	struct vga_config *vc;
724 	struct vga_handle *vh;
725 
726 	vc = &vga_console_vc;
727 	vh = &vc->hdl;
728 
729 	if (vgaconsole) {
730 		bus_space_unmap(vh->vh_iot, vh->vh_ioh_vga, 0x10);
731 		bus_space_unmap(vh->vh_iot, vh->vh_ioh_6845, 0x10);
732 
733 		return 1;
734 	}
735 
736 	return 0;
737 }
738 
739 int
740 vga_is_console(bus_space_tag_t iot, int type)
741 {
742 	if (vgaconsole &&
743 	    !vga_console_attached &&
744 	    bus_space_is_equal(iot, vga_console_vc.hdl.vh_iot) &&
745 	    (vga_console_type == -1 || (type == vga_console_type)))
746 		return (1);
747 	return (0);
748 }
749 
750 static int
751 vga_get_video(struct vga_config *vc)
752 {
753 
754 	return (vga_ts_read(&vc->hdl, mode) & VGA_TS_MODE_BLANK) == 0;
755 }
756 
757 static void
758 vga_set_video(struct vga_config *vc, int state)
759 {
760 	int val;
761 
762 	vga_ts_write(&vc->hdl, syncreset, 0x01);
763 	if (state) {					/* unblank screen */
764 		val = vga_ts_read(&vc->hdl, mode);
765 		vga_ts_write(&vc->hdl, mode, val & ~VGA_TS_MODE_BLANK);
766 #ifndef VGA_NO_VBLANK
767 		val = vga_6845_read(&vc->hdl, mode);
768 		vga_6845_write(&vc->hdl, mode, val | 0x80);
769 #endif
770 	} else {					/* blank screen */
771 		val = vga_ts_read(&vc->hdl, mode);
772 		vga_ts_write(&vc->hdl, mode, val | VGA_TS_MODE_BLANK);
773 #ifndef VGA_NO_VBLANK
774 		val = vga_6845_read(&vc->hdl, mode);
775 		vga_6845_write(&vc->hdl, mode, val & ~0x80);
776 #endif
777 	}
778 	vga_ts_write(&vc->hdl, syncreset, 0x03);
779 }
780 
781 static int
782 vga_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
783 {
784 	struct vga_config *vc = v;
785 	struct vgascreen *scr = vs;
786 	const struct vga_funcs *vf = vc->vc_funcs;
787 
788 	switch (cmd) {
789 	case WSDISPLAYIO_SMODE:
790 		if (*(u_int *)data == WSDISPLAYIO_MODE_EMUL)
791 			vga_restore_palette(vc);
792 		return 0;
793 
794 	case WSDISPLAYIO_GTYPE:
795 		*(int *)data = vc->vc_type;
796 		return 0;
797 
798 	case WSDISPLAYIO_GINFO:
799 		/* XXX should get detailed hardware information here */
800 		return EPASSTHROUGH;
801 
802 	case WSDISPLAYIO_GVIDEO:
803 		*(int *)data = (vga_get_video(vc) ?
804 		    WSDISPLAYIO_VIDEO_ON : WSDISPLAYIO_VIDEO_OFF);
805 		return 0;
806 
807 	case WSDISPLAYIO_SVIDEO:
808 		vga_set_video(vc, *(int *)data == WSDISPLAYIO_VIDEO_ON);
809 		return 0;
810 
811 	case WSDISPLAYIO_GETWSCHAR:
812 		KASSERT(scr != NULL);
813 		return pcdisplay_getwschar(&scr->pcs,
814 		    (struct wsdisplay_char *)data);
815 
816 	case WSDISPLAYIO_PUTWSCHAR:
817 		KASSERT(scr != NULL);
818 		return pcdisplay_putwschar(&scr->pcs,
819 		    (struct wsdisplay_char *)data);
820 
821 #ifdef WSDISPLAY_CUSTOM_BORDER
822 	case WSDISPLAYIO_GBORDER:
823 		return (vga_getborder(vc, (u_int *)data));
824 
825 	case WSDISPLAYIO_SBORDER:
826 		return (vga_setborder(vc, *(u_int *)data));
827 #endif
828 
829 	case WSDISPLAYIO_GETCMAP:
830 	case WSDISPLAYIO_PUTCMAP:
831 	case WSDISPLAYIO_GCURPOS:
832 	case WSDISPLAYIO_SCURPOS:
833 	case WSDISPLAYIO_GCURMAX:
834 	case WSDISPLAYIO_GCURSOR:
835 	case WSDISPLAYIO_SCURSOR:
836 		/* NONE of these operations are by the generic VGA driver. */
837 		return EPASSTHROUGH;
838 	}
839 
840 	if (vc->vc_funcs == NULL)
841 		return (EPASSTHROUGH);
842 
843 	if (vf->vf_ioctl == NULL)
844 		return (EPASSTHROUGH);
845 
846 	return ((*vf->vf_ioctl)(v, cmd, data, flag, l));
847 }
848 
849 static paddr_t
850 vga_mmap(void *v, void *vs, off_t offset, int prot)
851 {
852 	struct vga_config *vc = v;
853 	const struct vga_funcs *vf = vc->vc_funcs;
854 
855 	if (vc->vc_funcs == NULL)
856 		return (-1);
857 
858 	if (vf->vf_mmap == NULL)
859 		return (-1);
860 
861 	return ((*vf->vf_mmap)(v, offset, prot));
862 }
863 
864 static int
865 vga_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
866 		 int *curxp, int *curyp, long *defattrp)
867 {
868 	struct vga_config *vc = v;
869 	struct vgascreen *scr;
870 
871 	if (vc->nscreens == 1) {
872 		struct vgascreen *scr1 = vc->screens.lh_first;
873 		/*
874 		 * When allocating the second screen, get backing store
875 		 * for the first one too.
876 		 * XXX We could be more clever and use video RAM.
877 		 */
878 		scr1->pcs.mem =
879 		    malloc(scr1->pcs.type->ncols * scr1->pcs.type->nrows * 2,
880 		    M_DEVBUF, M_WAITOK);
881 	}
882 
883 	scr = malloc(sizeof(struct vgascreen), M_DEVBUF, M_WAITOK);
884 	vga_init_screen(vc, scr, type, vc->nscreens == 0, defattrp);
885 
886 	if (vc->nscreens > 1) {
887 		scr->pcs.mem = malloc(type->ncols * type->nrows * 2,
888 		    M_DEVBUF, M_WAITOK);
889 		pcdisplay_eraserows(&scr->pcs, 0, type->nrows, *defattrp);
890 	}
891 
892 	*cookiep = scr;
893 	*curxp = scr->pcs.cursorcol;
894 	*curyp = scr->pcs.cursorrow;
895 
896 	return (0);
897 }
898 
899 static void
900 vga_free_screen(void *v, void *cookie)
901 {
902 	struct vgascreen *vs = cookie;
903 	struct vga_config *vc = vs->cfg;
904 
905 	LIST_REMOVE(vs, next);
906 	vc->nscreens--;
907 	if (vs->fontset1)
908 		egavga_unreffont(vc, vs->fontset1);
909 	if (vs->fontset2)
910 		egavga_unreffont(vc, vs->fontset2);
911 
912 	if (vs != &vga_console_screen)
913 		free(vs, M_DEVBUF);
914 	else
915 		panic("vga_free_screen: console");
916 
917 	if (vc->active == vs)
918 		vc->active = 0;
919 }
920 
921 static void vga_usefont(struct vga_config *, struct egavga_font *);
922 
923 static void
924 vga_usefont(struct vga_config *vc, struct egavga_font *f)
925 {
926 	int slot;
927 	struct egavga_font *of;
928 
929 	if (f->slot != -1)
930 		goto toend;
931 
932 	for (slot = 0; slot < vc->vc_nfontslots; slot++) {
933 		if (!vc->vc_fonts[slot])
934 			goto loadit;
935 	}
936 
937 	/* have to kick out another one */
938 	TAILQ_FOREACH(of, &vc->vc_fontlist, next) {
939 		if (of->slot != -1) {
940 			KASSERT(vc->vc_fonts[of->slot] == of);
941 			slot = of->slot;
942 			of->slot = -1;
943 			goto loadit;
944 		}
945 	}
946 	panic("vga_usefont");
947 
948 loadit:
949 	vga_loadchars(&vc->hdl, slot, f->wsfont->firstchar,
950 	    f->wsfont->numchars, f->wsfont->fontheight, f->wsfont->data);
951 	f->slot = slot;
952 	vc->vc_fonts[slot] = f;
953 
954 toend:
955 	TAILQ_REMOVE(&vc->vc_fontlist, f, next);
956 	TAILQ_INSERT_TAIL(&vc->vc_fontlist, f, next);
957 }
958 
959 static void
960 vga_setfont(struct vga_config *vc, struct vgascreen *scr)
961 {
962 	int fontslot1, fontslot2;
963 
964 	if (scr->fontset1)
965 		vga_usefont(vc, scr->fontset1);
966 	if (scr->fontset2)
967 		vga_usefont(vc, scr->fontset2);
968 
969 	fontslot1 = (scr->fontset1 ? scr->fontset1->slot : 0);
970 	fontslot2 = (scr->fontset2 ? scr->fontset2->slot : fontslot1);
971 	if (vc->currentfontset1 != fontslot1 ||
972 	    vc->currentfontset2 != fontslot2) {
973 		vga_setfontset(&vc->hdl, fontslot1, fontslot2);
974 		vc->currentfontset1 = fontslot1;
975 		vc->currentfontset2 = fontslot2;
976 	}
977 }
978 
979 static int
980 vga_show_screen(void *v, void *cookie, int waitok,
981 		void (*cb)(void *, int, int), void *cbarg)
982 {
983 	struct vgascreen *scr = cookie, *oldscr;
984 	struct vga_config *vc = scr->cfg;
985 
986 	oldscr = vc->active; /* can be NULL! */
987 	if (scr == oldscr) {
988 		return (0);
989 	}
990 
991 	vc->wantedscreen = cookie;
992 	vc->switchcb = cb;
993 	vc->switchcbarg = cbarg;
994 	if (cb) {
995 		callout_reset(&vc->vc_switch_callout, 0,
996 		    (void(*)(void *))vga_doswitch, vc);
997 		return (EAGAIN);
998 	}
999 
1000 	vga_doswitch(vc);
1001 	return (0);
1002 }
1003 
1004 static void
1005 vga_doswitch(struct vga_config *vc)
1006 {
1007 	struct vgascreen *scr, *oldscr;
1008 	struct vga_handle *vh = &vc->hdl;
1009 	const struct wsscreen_descr *type;
1010 
1011 	scr = vc->wantedscreen;
1012 	if (!scr) {
1013 		printf("vga_doswitch: disappeared\n");
1014 		(*vc->switchcb)(vc->switchcbarg, EIO, 0);
1015 		return;
1016 	}
1017 	type = scr->pcs.type;
1018 	oldscr = vc->active; /* can be NULL! */
1019 #ifdef DIAGNOSTIC
1020 	if (oldscr) {
1021 		if (!oldscr->pcs.active)
1022 			panic("vga_show_screen: not active");
1023 		if (oldscr->pcs.type != vc->currenttype)
1024 			panic("vga_show_screen: bad type");
1025 	}
1026 #endif
1027 	if (scr == oldscr) {
1028 		return;
1029 	}
1030 #ifdef DIAGNOSTIC
1031 	if (scr->pcs.active)
1032 		panic("vga_show_screen: active");
1033 #endif
1034 
1035 	if (oldscr) {
1036 		const struct wsscreen_descr *oldtype = oldscr->pcs.type;
1037 
1038 		oldscr->pcs.active = 0;
1039 		bus_space_read_region_2(vh->vh_memt, vh->vh_memh,
1040 		    oldscr->pcs.dispoffset, oldscr->pcs.mem,
1041 		    oldtype->ncols * oldtype->nrows);
1042 	}
1043 
1044 	if (vc->currenttype != type) {
1045 		vga_setscreentype(vh, type);
1046 		vc->currenttype = type;
1047 	}
1048 
1049 	vga_setfont(vc, scr);
1050 	vga_restore_palette(vc);
1051 
1052 	scr->pcs.visibleoffset = scr->pcs.dispoffset = scr->mindispoffset;
1053 	if (!oldscr || (scr->pcs.dispoffset != oldscr->pcs.dispoffset)) {
1054 		vga_6845_write(vh, startadrh, scr->pcs.dispoffset >> 9);
1055 		vga_6845_write(vh, startadrl, scr->pcs.dispoffset >> 1);
1056 	}
1057 
1058 	bus_space_write_region_2(vh->vh_memt, vh->vh_memh,
1059 	    scr->pcs.dispoffset, scr->pcs.mem, type->ncols * type->nrows);
1060 	scr->pcs.active = 1;
1061 
1062 	vc->active = scr;
1063 
1064 	pcdisplay_cursor(&scr->pcs, scr->pcs.cursoron,
1065 	    scr->pcs.cursorrow, scr->pcs.cursorcol);
1066 
1067 	vc->wantedscreen = 0;
1068 	if (vc->switchcb)
1069 		(*vc->switchcb)(vc->switchcbarg, 0, 0);
1070 }
1071 
1072 static int
1073 vga_load_font(void *v, void *cookie, struct wsdisplay_font *data)
1074 {
1075 	struct vga_config *vc = v;
1076 	struct vgascreen *scr = cookie;
1077 	char *name2;
1078 	int res;
1079 
1080 	if (scr) {
1081 		name2 = NULL;
1082 		if (data->name) {
1083 			name2 = strchr(data->name, ',');
1084 			if (name2)
1085 				*name2++ = '\0';
1086 		}
1087 		res = vga_selectfont(vc, scr, data->name, name2);
1088 		if (!res && scr->pcs.active)
1089 			vga_setfont(vc, scr);
1090 		return (res);
1091 	}
1092 
1093 	return (0);
1094 }
1095 
1096 static int
1097 vga_allocattr(void *id, int fg, int bg, int flags, long *attrp)
1098 {
1099 	struct vgascreen *scr = id;
1100 	struct vga_config *vc = scr->cfg;
1101 
1102 	if (__predict_false((unsigned int)fg >= sizeof(fgansitopc) ||
1103 	    (unsigned int)bg >= sizeof(bgansitopc)))
1104 		return (EINVAL);
1105 
1106 	if (vc->hdl.vh_mono) {
1107 		if (flags & WSATTR_WSCOLORS)
1108 			return (EINVAL);
1109 		if (flags & WSATTR_REVERSE)
1110 			*attrp = 0x70;
1111 		else
1112 			*attrp = 0x07;
1113 		if (flags & WSATTR_UNDERLINE)
1114 			*attrp |= FG_UNDERLINE;
1115 		if (flags & WSATTR_HILIT)
1116 			*attrp |= FG_INTENSE;
1117 	} else {
1118 		if (flags & (WSATTR_UNDERLINE | WSATTR_REVERSE))
1119 			return (EINVAL);
1120 		if (flags & WSATTR_WSCOLORS)
1121 			*attrp = fgansitopc[fg] | bgansitopc[bg];
1122 		else
1123 			*attrp = 7;
1124 		if (flags & WSATTR_HILIT)
1125 			*attrp += 8;
1126 	}
1127 	if (flags & WSATTR_BLINK)
1128 		*attrp |= FG_BLINK;
1129 	return (0);
1130 }
1131 
1132 static void
1133 vga_copyrows(void *id, int srcrow, int dstrow, int nrows)
1134 {
1135 	struct vgascreen *scr = id;
1136 	bus_space_tag_t memt = scr->pcs.hdl->ph_memt;
1137 	bus_space_handle_t memh = scr->pcs.hdl->ph_memh;
1138 	int ncols = scr->pcs.type->ncols;
1139 	bus_size_t srcoff, dstoff;
1140 
1141 	srcoff = srcrow * ncols + 0;
1142 	dstoff = dstrow * ncols + 0;
1143 
1144 	if (scr->pcs.active) {
1145 		if (dstrow == 0 && (srcrow + nrows == scr->pcs.type->nrows)) {
1146 #ifdef PCDISPLAY_SOFTCURSOR
1147 			int cursoron = scr->pcs.cursoron;
1148 
1149 			if (cursoron)
1150 				pcdisplay_cursor(&scr->pcs, 0,
1151 				    scr->pcs.cursorrow, scr->pcs.cursorcol);
1152 #endif
1153 			/* scroll up whole screen */
1154 			if ((scr->pcs.dispoffset + srcrow * ncols * 2)
1155 			    <= scr->maxdispoffset) {
1156 				scr->pcs.dispoffset += srcrow * ncols * 2;
1157 			} else {
1158 				bus_space_copy_region_2(memt, memh,
1159 				    scr->pcs.dispoffset + srcoff * 2,
1160 				    memh, scr->mindispoffset, nrows * ncols);
1161 				scr->pcs.dispoffset = scr->mindispoffset;
1162 			}
1163 			vga_6845_write(&scr->cfg->hdl, startadrh,
1164 			    scr->pcs.dispoffset >> 9);
1165 			vga_6845_write(&scr->cfg->hdl, startadrl,
1166 			    scr->pcs.dispoffset >> 1);
1167 #ifdef PCDISPLAY_SOFTCURSOR
1168 			if (cursoron)
1169 				pcdisplay_cursor(&scr->pcs, 1,
1170 				    scr->pcs.cursorrow, scr->pcs.cursorcol);
1171 #endif
1172 		} else {
1173 			bus_space_copy_region_2(memt, memh,
1174 			    scr->pcs.dispoffset + srcoff * 2,
1175 			    memh, scr->pcs.dispoffset + dstoff * 2,
1176 			    nrows * ncols);
1177 		}
1178 	} else
1179 		memcpy(&scr->pcs.mem[dstoff], &scr->pcs.mem[srcoff],
1180 		    nrows * ncols * 2);
1181 }
1182 
1183 #ifdef WSCONS_SUPPORT_PCVTFONTS
1184 
1185 #define NOTYET 0xffff
1186 static const uint16_t pcvt_unichars[0xa0] = {
1187 /* 0 */	_e006U, /* N/L control */
1188 	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
1189 	NOTYET,
1190 	0x2409, /* SYMBOL FOR HORIZONTAL TABULATION */
1191 	0x240a, /* SYMBOL FOR LINE FEED */
1192 	0x240b, /* SYMBOL FOR VERTICAL TABULATION */
1193 	0x240c, /* SYMBOL FOR FORM FEED */
1194 	0x240d, /* SYMBOL FOR CARRIAGE RETURN */
1195 	NOTYET, NOTYET,
1196 /* 1 */	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
1197 	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
1198 /* 2 */	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
1199 	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
1200 /* 3 */	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
1201 	NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET, NOTYET,
1202 /* 4 */	0x03c1, /* GREEK SMALL LETTER RHO */
1203 	0x03c8, /* GREEK SMALL LETTER PSI */
1204 	0x2202, /* PARTIAL DIFFERENTIAL */
1205 	0x03bb, /* GREEK SMALL LETTER LAMDA */
1206 	0x03b9, /* GREEK SMALL LETTER IOTA */
1207 	0x03b7, /* GREEK SMALL LETTER ETA */
1208 	0x03b5, /* GREEK SMALL LETTER EPSILON */
1209 	0x03c7, /* GREEK SMALL LETTER CHI */
1210 	0x2228, /* LOGICAL OR */
1211 	0x2227, /* LOGICAL AND */
1212 	0x222a, /* UNION */
1213 	0x2283, /* SUPERSET OF */
1214 	0x2282, /* SUBSET OF */
1215 	0x03a5, /* GREEK CAPITAL LETTER UPSILON */
1216 	0x039e, /* GREEK CAPITAL LETTER XI */
1217 	0x03a8, /* GREEK CAPITAL LETTER PSI */
1218 /* 5 */	0x03a0, /* GREEK CAPITAL LETTER PI */
1219 	0x21d2, /* RIGHTWARDS DOUBLE ARROW */
1220 	0x21d4, /* LEFT RIGHT DOUBLE ARROW */
1221 	0x039b, /* GREEK CAPITAL LETTER LAMDA */
1222 	0x0398, /* GREEK CAPITAL LETTER THETA */
1223 	0x2243, /* ASYMPTOTICALLY EQUAL TO */
1224 	0x2207, /* NABLA */
1225 	0x2206, /* INCREMENT */
1226 	0x221d, /* PROPORTIONAL TO */
1227 	0x2234, /* THEREFORE */
1228 	0x222b, /* INTEGRAL */
1229 	0x2215, /* DIVISION SLASH */
1230 	0x2216, /* SET MINUS */
1231 	_e00eU, /* angle? */
1232 	_e00dU, /* inverted angle? */
1233 	_e00bU, /* braceleftmid */
1234 /* 6 */	_e00cU, /* bracerightmid */
1235 	_e007U, /* bracelefttp */
1236 	_e008U, /* braceleftbt */
1237 	_e009U, /* bracerighttp */
1238 	_e00aU, /* bracerightbt */
1239 	0x221a, /* SQUARE ROOT */
1240 	0x03c9, /* GREEK SMALL LETTER OMEGA */
1241 	0x00a5, /* YEN SIGN */
1242 	0x03be, /* GREEK SMALL LETTER XI */
1243 	0x00fd, /* LATIN SMALL LETTER Y WITH ACUTE */
1244 	0x00fe, /* LATIN SMALL LETTER THORN */
1245 	0x00f0, /* LATIN SMALL LETTER ETH */
1246 	0x00de, /* LATIN CAPITAL LETTER THORN */
1247 	0x00dd, /* LATIN CAPITAL LETTER Y WITH ACUTE */
1248 	0x00d7, /* MULTIPLICATION SIGN */
1249 	0x00d0, /* LATIN CAPITAL LETTER ETH */
1250 /* 7 */	0x00be, /* VULGAR FRACTION THREE QUARTERS */
1251 	0x00b8, /* CEDILLA */
1252 	0x00b4, /* ACUTE ACCENT */
1253 	0x00af, /* MACRON */
1254 	0x00ae, /* REGISTERED SIGN */
1255 	0x00ad, /* SOFT HYPHEN */
1256 	0x00ac, /* NOT SIGN */
1257 	0x00a8, /* DIAERESIS */
1258 	0x2260, /* NOT EQUAL TO */
1259 	0x23bd, /* scan 9 */
1260 	0x23bc, /* scan 7 */
1261 	0x2500, /* scan 5 */
1262 	0x23bb, /* scan 3 */
1263 	0x23ba, /* scan 1 */
1264 	0x03c5, /* GREEK SMALL LETTER UPSILON */
1265 	0x00f8, /* LATIN SMALL LETTER O WITH STROKE */
1266 /* 8 */	0x0153, /* LATIN SMALL LIGATURE OE */
1267 	0x00f5, /* LATIN SMALL LETTER O WITH TILDE !!!doc bug */
1268 	0x00e3, /* LATIN SMALL LETTER A WITH TILDE */
1269 	0x0178, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */
1270 	0x00db, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
1271 	0x00da, /* LATIN CAPITAL LETTER U WITH ACUTE */
1272 	0x00d9, /* LATIN CAPITAL LETTER U WITH GRAVE */
1273 	0x00d8, /* LATIN CAPITAL LETTER O WITH STROKE */
1274 	0x0152, /* LATIN CAPITAL LIGATURE OE */
1275 	0x00d5, /* LATIN CAPITAL LETTER O WITH TILDE */
1276 	0x00d4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
1277 	0x00d3, /* LATIN CAPITAL LETTER O WITH ACUTE */
1278 	0x00d2, /* LATIN CAPITAL LETTER O WITH GRAVE */
1279 	0x00cf, /* LATIN CAPITAL LETTER I WITH DIAERESIS */
1280 	0x00ce, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
1281 	0x00cd, /* LATIN CAPITAL LETTER I WITH ACUTE */
1282 /* 9 */	0x00cc, /* LATIN CAPITAL LETTER I WITH GRAVE */
1283 	0x00cb, /* LATIN CAPITAL LETTER E WITH DIAERESIS */
1284 	0x00ca, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
1285 	0x00c8, /* LATIN CAPITAL LETTER E WITH GRAVE */
1286 	0x00c3, /* LATIN CAPITAL LETTER A WITH TILDE */
1287 	0x00c2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
1288 	0x00c1, /* LATIN CAPITAL LETTER A WITH ACUTE */
1289 	0x00c0, /* LATIN CAPITAL LETTER A WITH GRAVE */
1290 	0x00b9, /* SUPERSCRIPT ONE */
1291 	0x00b7, /* MIDDLE DOT */
1292 	0x03b6, /* GREEK SMALL LETTER ZETA */
1293 	0x00b3, /* SUPERSCRIPT THREE */
1294 	0x00a9, /* COPYRIGHT SIGN */
1295 	0x00a4, /* CURRENCY SIGN */
1296 	0x03ba, /* GREEK SMALL LETTER KAPPA */
1297 	_e000U  /* mirrored question mark? */
1298 };
1299 
1300 static int vga_pcvt_mapchar(int, u_int *);
1301 
1302 static int
1303 vga_pcvt_mapchar(int uni, u_int *index)
1304 {
1305 	int i;
1306 
1307 	for (i = 0; i < 0xa0; i++) /* 0xa0..0xff are reserved */
1308 		if (uni == pcvt_unichars[i]) {
1309 			*index = i;
1310 			return (5);
1311 		}
1312 	*index = 0x99; /* middle dot */
1313 	return (0);
1314 }
1315 
1316 #endif /* WSCONS_SUPPORT_PCVTFONTS */
1317 
1318 #ifdef WSCONS_SUPPORT_ISO7FONTS
1319 
1320 static int
1321 vga_iso7_mapchar(int uni, u_int *index)
1322 {
1323 
1324 	/*
1325 	 * U+0384 (GREEK TONOS) to
1326 	 * U+03ce (GREEK SMALL LETTER OMEGA WITH TONOS)
1327 	 * map directly to the iso-9 font
1328 	 */
1329 	if (uni >= 0x0384 && uni <= 0x03ce) {
1330 		/* U+0384 is at offset 0xb4 in the font */
1331 		*index = uni - 0x0384 + 0xb4;
1332 		return (5);
1333 	}
1334 
1335 	/* XXX more chars in the iso-9 font */
1336 
1337 	*index = 0xa4; /* shaded rectangle */
1338 	return (0);
1339 }
1340 
1341 #endif /* WSCONS_SUPPORT_ISO7FONTS */
1342 
1343 static const uint16_t iso2_unichars[0x60] = {
1344 	0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7,
1345 	0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B,
1346 	0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7,
1347 	0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C,
1348 	0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
1349 	0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
1350 	0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
1351 	0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
1352 	0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
1353 	0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
1354 	0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
1355 	0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
1356 };
1357 
1358 static const uint16_t koi8_unichars[0x40] = {
1359 	0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
1360 	0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
1361 	0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
1362 	0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A,
1363 	0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
1364 	0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
1365 	0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
1366 	0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A
1367 };
1368 
1369 static int _vga_mapchar(void *, const struct egavga_font *, int, u_int *);
1370 
1371 static int
1372 _vga_mapchar(void *id, const struct egavga_font *font, int uni, u_int *index)
1373 {
1374 
1375 	switch (font->wsfont->encoding) {
1376 	case WSDISPLAY_FONTENC_ISO:
1377 		if (uni < 256) {
1378 			*index = uni;
1379 			return (5);
1380 		} else {
1381 			*index = ' ';
1382 			return (0);
1383 		}
1384 	case WSDISPLAY_FONTENC_ISO2:
1385 		if (uni < 0xa0) {
1386 			*index = uni;
1387 			return (5);
1388 		} else {
1389 			int i;
1390 			for (i = 0; i < 0x60; i++) {
1391 				if (uni == iso2_unichars[i]) {
1392 					*index = i + 0xa0;
1393 					return (5);
1394 				}
1395 			}
1396 			*index = 0xa4; /* currency sign */
1397 			return (0);
1398 		}
1399 	case WSDISPLAY_FONTENC_KOI8_R:
1400 		if (uni < 0x80) {
1401 			*index = uni;
1402 			return (5);
1403 		} else {
1404 			int i;
1405 			for (i = 0; i < 0x40; i++) {
1406 				if (uni == koi8_unichars[i]) {
1407 					*index = i + 0xc0;
1408 					return (5);
1409 				}
1410 			}
1411 			*index = 0x94; /* box */
1412 			return (0);
1413 		}
1414 	case WSDISPLAY_FONTENC_IBM:
1415 		return (pcdisplay_mapchar(id, uni, index));
1416 #ifdef WSCONS_SUPPORT_PCVTFONTS
1417 	case WSDISPLAY_FONTENC_PCVT:
1418 		return (vga_pcvt_mapchar(uni, index));
1419 #endif
1420 #ifdef WSCONS_SUPPORT_ISO7FONTS
1421 	case WSDISPLAY_FONTENC_ISO7:
1422 		return (vga_iso7_mapchar(uni, index));
1423 #endif
1424 	default:
1425 #ifdef VGAFONTDEBUG
1426 		printf("_vga_mapchar: encoding=%d\n", font->wsfont->encoding);
1427 #endif
1428 		*index = ' ';
1429 		return (0);
1430 	}
1431 }
1432 
1433 static int
1434 vga_mapchar(void *id, int uni, u_int *index)
1435 {
1436 	struct vgascreen *scr = id;
1437 	u_int idx1, idx2;
1438 	int res1, res2;
1439 
1440 	res1 = 0;
1441 	idx1 = ' '; /* space */
1442 	if (scr->fontset1)
1443 		res1 = _vga_mapchar(id, scr->fontset1, uni, &idx1);
1444 	res2 = -1;
1445 	if (scr->fontset2) {
1446 		KASSERT(VGA_SCREEN_CANTWOFONTS(scr->pcs.type));
1447 		res2 = _vga_mapchar(id, scr->fontset2, uni, &idx2);
1448 	}
1449 	if (res2 > res1) {
1450 		*index = idx2 | 0x0800; /* attribute bit 3 */
1451 		return (res2);
1452 	}
1453 	*index = idx1;
1454 	return (res1);
1455 }
1456 
1457 #ifdef WSDISPLAY_SCROLLSUPPORT
1458 static void
1459 vga_scroll(void *v, void *cookie, int lines)
1460 {
1461 	struct vga_config *vc = v;
1462 	struct vgascreen *scr = cookie;
1463 	struct vga_handle *vh = &vc->hdl;
1464 
1465 	if (lines == 0) {
1466 		if (scr->pcs.visibleoffset == scr->pcs.dispoffset)
1467 			return;
1468 
1469 		scr->pcs.visibleoffset = scr->pcs.dispoffset;
1470 	}
1471 	else {
1472 		int vga_scr_end;
1473 		int margin = scr->pcs.type->ncols * 2;
1474 		int ul, we, p, st;
1475 
1476 		vga_scr_end = (scr->pcs.dispoffset + scr->pcs.type->ncols *
1477 		    scr->pcs.type->nrows * 2);
1478 		if (scr->vga_rollover > vga_scr_end + margin) {
1479 			ul = vga_scr_end;
1480 			we = scr->vga_rollover + scr->pcs.type->ncols * 2;
1481 		} else {
1482 			ul = 0;
1483 			we = 0x8000;
1484 		}
1485 		p = (scr->pcs.visibleoffset - ul + we) % we + lines *
1486 		    (scr->pcs.type->ncols * 2);
1487 		st = (scr->pcs.dispoffset - ul + we) % we;
1488 		if (p < margin)
1489 			p = 0;
1490 		if (p > st - margin)
1491 			p = st;
1492 		scr->pcs.visibleoffset = (p + ul) % we;
1493 	}
1494 
1495 	vga_6845_write(vh, startadrh, scr->pcs.visibleoffset >> 9);
1496 	vga_6845_write(vh, startadrl, scr->pcs.visibleoffset >> 1);
1497 }
1498 #endif
1499 
1500 static void
1501 vga_putchar(void *c, int row, int col, u_int uc, long attr)
1502 {
1503 
1504 	pcdisplay_putchar(c, row, col, uc, attr);
1505 }
1506 
1507 #ifdef WSDISPLAY_CUSTOM_BORDER
1508 static int
1509 vga_getborder(struct vga_config *vc, u_int *valuep)
1510 {
1511 	struct vga_handle *vh = &vc->hdl;
1512 	u_int idx;
1513 	uint8_t value;
1514 
1515 	if (vh->vh_mono)
1516 		return ENODEV;
1517 
1518 	value = _vga_attr_read(vh, VGA_ATC_OVERSCAN);
1519 	for (idx = 0; idx < sizeof(fgansitopc); idx++) {
1520 		if (fgansitopc[idx] == value) {
1521 			*valuep = idx;
1522 			return (0);
1523 		}
1524 	}
1525 	return (EIO);
1526 }
1527 
1528 static int
1529 vga_setborder(struct vga_config *vc, u_int value)
1530 {
1531 	struct vga_handle *vh = &vc->hdl;
1532 
1533 	if (vh->vh_mono)
1534 		return ENODEV;
1535 	if (value >= sizeof(fgansitopc))
1536 		return EINVAL;
1537 
1538 	_vga_attr_write(vh, VGA_ATC_OVERSCAN, fgansitopc[value]);
1539 	return (0);
1540 }
1541 #endif /* WSDISPLAY_CUSTOM_BORDER */
1542 
1543 void
1544 vga_resume(struct vga_softc *sc)
1545 {
1546 #ifdef VGA_RESET_ON_RESUME
1547 	vga_initregs(&sc->sc_vc->hdl);
1548 #endif
1549 #ifdef PCDISPLAY_SOFTCURSOR
1550 	/* Disable the hardware cursor */
1551 	vga_6845_write(&sc->sc_vc->hdl, curstart, 0x20);
1552 	vga_6845_write(&sc->sc_vc->hdl, curend, 0x00);
1553 #endif
1554 }
1555 
1556 static void
1557 vga_save_palette(struct vga_config *vc)
1558 {
1559 	struct vga_handle *vh = &vc->hdl;
1560 	size_t i;
1561 	uint8_t *palette = vc->palette;
1562 
1563 	if (vh->vh_mono)
1564 		return;
1565 
1566 	vga_raw_write(vh, VGA_DAC_PELMASK, 0xff);
1567 	vga_raw_write(vh, VGA_DAC_ADDRR, 0x00);
1568 	for (i = 0; i < sizeof(vc->palette); i++)
1569 		*palette++ = vga_raw_read(vh, VGA_DAC_PALETTE);
1570 
1571 	vga_reset_state(vh);			/* reset flip/flop */
1572 }
1573 
1574 static void
1575 vga_restore_palette(struct vga_config *vc)
1576 {
1577 	struct vga_handle *vh = &vc->hdl;
1578 	size_t i;
1579 	uint8_t *palette = vc->palette;
1580 
1581 	if (vh->vh_mono)
1582 		return;
1583 
1584 	vga_raw_write(vh, VGA_DAC_PELMASK, 0xff);
1585 	vga_raw_write(vh, VGA_DAC_ADDRW, 0x00);
1586 	for (i = 0; i < sizeof(vc->palette); i++)
1587 		vga_raw_write(vh, VGA_DAC_PALETTE, *palette++);
1588 
1589 	vga_reset_state(vh);			/* reset flip/flop */
1590 	vga_enable(vh);
1591 }
1592