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