xref: /illumos-gate/usr/src/uts/i86pc/io/gfx_private/gfxp_vgatext.c (revision 933ae53f0bf0708d7bf2756d3f21936a0d5fad82)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
29 /*		All Rights Reserved	*/
30 
31 #include <sys/errno.h>
32 #include <sys/types.h>
33 #include <sys/conf.h>
34 #include <sys/kmem.h>
35 #include <sys/visual_io.h>
36 #include <sys/font.h>
37 #include <sys/fbio.h>
38 #include <sys/ddi.h>
39 #include <sys/stat.h>
40 #include <sys/sunddi.h>
41 #include <sys/open.h>
42 #include <sys/modctl.h>
43 #include <sys/pci.h>
44 #include <sys/kd.h>
45 #include <sys/ddi_impldefs.h>
46 #include <sys/gfx_private.h>
47 #include "gfxp_fb.h"
48 
49 #define	MYNAME	"gfxp_vgatext"
50 
51 typedef enum pc_colors {
52 	pc_black	= 0,
53 	pc_blue		= 1,
54 	pc_green	= 2,
55 	pc_cyan		= 3,
56 	pc_red		= 4,
57 	pc_magenta	= 5,
58 	pc_brown	= 6,
59 	pc_white	= 7,
60 	pc_grey		= 8,
61 	pc_brt_blue	= 9,
62 	pc_brt_green	= 10,
63 	pc_brt_cyan	= 11,
64 	pc_brt_red	= 12,
65 	pc_brt_magenta	= 13,
66 	pc_yellow	= 14,
67 	pc_brt_white	= 15
68 } pc_colors_t;
69 
70 static const unsigned char solaris_color_to_pc_color[16] = {
71 	pc_brt_white,		/*  0 - brt_white	*/
72 	pc_black,		/*  1 - black		*/
73 	pc_blue,		/*  2 - blue		*/
74 	pc_green,		/*  3 - green		*/
75 	pc_cyan,		/*  4 - cyan		*/
76 	pc_red,			/*  5 - red		*/
77 	pc_magenta,		/*  6 - magenta		*/
78 	pc_brown,		/*  7 - brown		*/
79 	pc_white,		/*  8 - white		*/
80 	pc_grey,		/*  9 - gery		*/
81 	pc_brt_blue,		/* 10 - brt_blue	*/
82 	pc_brt_green,		/* 11 - brt_green	*/
83 	pc_brt_cyan,		/* 12 - brt_cyan	*/
84 	pc_brt_red,		/* 13 - brt_red		*/
85 	pc_brt_magenta,		/* 14 - brt_magenta	*/
86 	pc_yellow		/* 15 - yellow		*/
87 };
88 
89 static ddi_device_acc_attr_t dev_attr = {
90 	DDI_DEVICE_ATTR_V0,
91 	DDI_NEVERSWAP_ACC,
92 	DDI_STRICTORDER_ACC,
93 };
94 
95 /* default structure for FBIOGATTR ioctl */
96 static struct fbgattr vgatext_attr =  {
97 /*	real_type	owner */
98 	FBTYPE_SUNFAST_COLOR, 0,
99 /* fbtype: type		h  w  depth cms  size */
100 	{ FBTYPE_SUNFAST_COLOR, VGA_TEXT_ROWS, VGA_TEXT_COLS, 1,    256,  0 },
101 /* fbsattr: flags emu_type	dev_specific */
102 	{ 0, FBTYPE_SUN4COLOR, { 0 } },
103 /*	emu_types */
104 	{ -1 }
105 };
106 
107 static struct vis_identifier gfxp_vgatext_ident = { "illumos_text" };
108 
109 static int vgatext_devinit(struct gfxp_fb_softc *, struct vis_devinit *data);
110 static void	vgatext_cons_copy(struct gfxp_fb_softc *,
111 			struct vis_conscopy *);
112 static void	vgatext_cons_display(struct gfxp_fb_softc *,
113 			struct vis_consdisplay *);
114 static int	vgatext_cons_clear(struct gfxp_fb_softc *,
115 			struct vis_consclear *);
116 static void	vgatext_cons_cursor(struct gfxp_fb_softc *,
117 			struct vis_conscursor *);
118 static void	vgatext_polled_copy(struct vis_polledio_arg *,
119 			struct vis_conscopy *);
120 static void	vgatext_polled_display(struct vis_polledio_arg *,
121 			struct vis_consdisplay *);
122 static void	vgatext_polled_cursor(struct vis_polledio_arg *,
123 			struct vis_conscursor *);
124 static void	vgatext_init(struct gfxp_fb_softc *);
125 static void	vgatext_set_text(struct gfxp_fb_softc *);
126 
127 static void	vgatext_get_text(struct gfxp_fb_softc *softc);
128 static void	vgatext_save_text(struct gfxp_fb_softc *softc);
129 static void	vgatext_kdsettext(struct gfxp_fb_softc *softc);
130 static int	vgatext_suspend(struct gfxp_fb_softc *softc);
131 static void	vgatext_resume(struct gfxp_fb_softc *softc);
132 static int	vgatext_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
133     size_t *, uint_t, void *);
134 
135 #if	defined(USE_BORDERS)
136 static void	vgatext_init_graphics(struct gfxp_fb_softc *);
137 #endif
138 
139 static int vgatext_kdsetmode(struct gfxp_fb_softc *softc, int mode);
140 static void vgatext_setfont(struct gfxp_fb_softc *softc);
141 static void vgatext_get_cursor(struct gfxp_fb_softc *softc,
142     screen_pos_t *row, screen_pos_t *col);
143 static void vgatext_set_cursor(struct gfxp_fb_softc *softc, int row, int col);
144 static void vgatext_hide_cursor(struct gfxp_fb_softc *softc);
145 static void vgatext_save_colormap(struct gfxp_fb_softc *softc);
146 static void vgatext_restore_colormap(struct gfxp_fb_softc *softc);
147 static int vgatext_get_pci_reg_index(dev_info_t *const devi,
148     unsigned long himask, unsigned long hival, unsigned long addr,
149 		off_t *offset);
150 static int vgatext_get_isa_reg_index(dev_info_t *const devi,
151 		unsigned long hival, unsigned long addr, off_t *offset);
152 
153 static struct gfxp_ops gfxp_vgatext_ops = {
154 	.ident = &gfxp_vgatext_ident,
155 	.kdsetmode = vgatext_kdsetmode,
156 	.devinit = vgatext_devinit,
157 	.cons_copy = vgatext_cons_copy,
158 	.cons_display = vgatext_cons_display,
159 	.cons_cursor = vgatext_cons_cursor,
160 	.cons_clear = vgatext_cons_clear,
161 	.suspend = vgatext_suspend,
162 	.resume = vgatext_resume,
163 	.devmap = vgatext_devmap
164 };
165 
166 #define	STREQ(a, b)	(strcmp((a), (b)) == 0)
167 
168 int
169 gfxp_vga_attach(dev_info_t *devi, struct gfxp_fb_softc *softc)
170 {
171 	int	unit = ddi_get_instance(devi);
172 	int	error;
173 	char	*parent_type = NULL;
174 	int	reg_rnumber;
175 	off_t	reg_offset;
176 	off_t	mem_offset;
177 	char	*cons;
178 	struct gfx_vga *vga;
179 
180 
181 	softc->polledio.display = vgatext_polled_display;
182 	softc->polledio.copy = vgatext_polled_copy;
183 	softc->polledio.cursor = vgatext_polled_cursor;
184 	softc->gfxp_ops = &gfxp_vgatext_ops;
185 	softc->fbgattr = &vgatext_attr;
186 	vga = kmem_zalloc(sizeof (*vga), KM_SLEEP);
187 	softc->console = (union gfx_console *)vga;
188 
189 	error = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(devi),
190 	    DDI_PROP_DONTPASS, "device_type", &parent_type);
191 	if (error != DDI_SUCCESS) {
192 		cmn_err(CE_WARN, MYNAME ": can't determine parent type.");
193 		goto fail;
194 	}
195 
196 	/* Not enable AGP and DRM by default */
197 	if (STREQ(parent_type, "isa") || STREQ(parent_type, "eisa")) {
198 		reg_rnumber = vgatext_get_isa_reg_index(devi, 1, VGA_REG_ADDR,
199 		    &reg_offset);
200 		if (reg_rnumber < 0) {
201 			cmn_err(CE_WARN,
202 			    MYNAME
203 			    ": can't find reg entry for registers");
204 			error = DDI_FAILURE;
205 			goto fail;
206 		}
207 		vga->fb_regno = vgatext_get_isa_reg_index(devi, 0,
208 		    VGA_MEM_ADDR, &mem_offset);
209 		if (vga->fb_regno < 0) {
210 			cmn_err(CE_WARN,
211 			    MYNAME ": can't find reg entry for memory");
212 			error = DDI_FAILURE;
213 			goto fail;
214 		}
215 	} else if (STREQ(parent_type, "pci") || STREQ(parent_type, "pciex")) {
216 		reg_rnumber = vgatext_get_pci_reg_index(devi,
217 		    PCI_REG_ADDR_M|PCI_REG_REL_M,
218 		    PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR,
219 		    &reg_offset);
220 		if (reg_rnumber < 0) {
221 			cmn_err(CE_WARN,
222 			    MYNAME ": can't find reg entry for registers");
223 			error = DDI_FAILURE;
224 			goto fail;
225 		}
226 		vga->fb_regno = vgatext_get_pci_reg_index(devi,
227 		    PCI_REG_ADDR_M|PCI_REG_REL_M,
228 		    PCI_ADDR_MEM32|PCI_RELOCAT_B, VGA_MEM_ADDR,
229 		    &mem_offset);
230 		if (vga->fb_regno < 0) {
231 			cmn_err(CE_WARN,
232 			    MYNAME ": can't find reg entry for memory");
233 			error = DDI_FAILURE;
234 			goto fail;
235 		}
236 	} else {
237 		cmn_err(CE_WARN, MYNAME ": unknown parent type \"%s\".",
238 		    parent_type);
239 		error = DDI_FAILURE;
240 		goto fail;
241 	}
242 	ddi_prop_free(parent_type);
243 	parent_type = NULL;
244 
245 	error = ddi_regs_map_setup(devi, reg_rnumber,
246 	    (caddr_t *)&vga->regs.addr, reg_offset, VGA_REG_SIZE,
247 	    &dev_attr, &vga->regs.handle);
248 	if (error != DDI_SUCCESS)
249 		goto fail;
250 	vga->regs.mapped = B_TRUE;
251 
252 	vga->fb_size = VGA_MEM_SIZE;
253 
254 	error = ddi_regs_map_setup(devi, vga->fb_regno,
255 	    (caddr_t *)&vga->fb.addr, mem_offset, vga->fb_size, &dev_attr,
256 	    &vga->fb.handle);
257 	if (error != DDI_SUCCESS)
258 		goto fail;
259 	vga->fb.mapped = B_TRUE;
260 
261 	if (ddi_get8(vga->regs.handle,
262 	    vga->regs.addr + VGA_MISC_R) & VGA_MISC_IOA_SEL)
263 		vga->text_base = (caddr_t)vga->fb.addr + VGA_COLOR_BASE;
264 	else
265 		vga->text_base = (caddr_t)vga->fb.addr + VGA_MONO_BASE;
266 
267 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
268 	    DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) {
269 		if (strcmp(cons, "graphics") == 0) {
270 			softc->happyface_boot = 1;
271 			softc->silent = 1;
272 			vga->current_base = vga->shadow;
273 		} else {
274 			vga->current_base = vga->text_base;
275 		}
276 		ddi_prop_free(cons);
277 	} else {
278 		vga->current_base = vga->text_base;
279 	}
280 
281 	/* Set cursor info. */
282 	vga->cursor.visible = fb_info.cursor.visible;
283 	vga->cursor.row = fb_info.cursor.pos.y;
284 	vga->cursor.col = fb_info.cursor.pos.x;
285 
286 	error = ddi_prop_create(makedevice(DDI_MAJOR_T_UNKNOWN, unit),
287 	    devi, DDI_PROP_CANSLEEP, DDI_KERNEL_IOCTL, NULL, 0);
288 	if (error != DDI_SUCCESS)
289 		goto fail;
290 
291 	/* only do this if not in graphics mode */
292 	if ((softc->silent == 0) && (GFXP_IS_CONSOLE(softc))) {
293 		vgatext_init(softc);
294 		vgatext_save_colormap(softc);
295 	}
296 
297 	return (DDI_SUCCESS);
298 
299 fail:
300 	kmem_free(vga, sizeof (*vga));
301 	if (parent_type != NULL)
302 		ddi_prop_free(parent_type);
303 	return (error);
304 }
305 
306 int
307 gfxp_vga_detach(dev_info_t *devi __unused, struct gfxp_fb_softc *softc)
308 {
309 	if (softc->console->vga.fb.mapped)
310 		ddi_regs_map_free(&softc->console->vga.fb.handle);
311 	if (softc->console->vga.regs.mapped)
312 		ddi_regs_map_free(&softc->console->vga.regs.handle);
313 	kmem_free(softc->console, sizeof (struct gfx_vga));
314 	return (DDI_SUCCESS);
315 }
316 
317 /*
318  * vgatext_save_text
319  * vgatext_suspend
320  * vgatext_resume
321  *
322  *	Routines to save and restore contents of the VGA text area
323  * Mostly, this is to support Suspend/Resume operation for graphics
324  * device drivers.  Here in the VGAtext common code, we simply squirrel
325  * away the contents of the hardware's text area during Suspend and then
326  * put it back during Resume
327  */
328 static void
329 vgatext_save_text(struct gfxp_fb_softc *softc)
330 {
331 	union gfx_console *console = softc->console;
332 	unsigned i;
333 
334 	for (i = 0; i < sizeof (console->vga.shadow); i++)
335 		console->vga.shadow[i] = console->vga.current_base[i];
336 }
337 
338 static int
339 vgatext_suspend(struct gfxp_fb_softc *softc)
340 {
341 	switch (softc->mode) {
342 	case KD_TEXT:
343 		vgatext_save_text(softc);
344 		break;
345 
346 	case KD_GRAPHICS:
347 		break;
348 
349 	default:
350 		cmn_err(CE_WARN, MYNAME ": unknown mode in vgatext_suspend.");
351 		return (DDI_FAILURE);
352 	}
353 	return (DDI_SUCCESS);
354 }
355 
356 static void
357 vgatext_resume(struct gfxp_fb_softc *softc)
358 {
359 
360 	switch (softc->mode) {
361 	case KD_TEXT:
362 		vgatext_kdsettext(softc);
363 		break;
364 
365 	case KD_GRAPHICS:
366 
367 		/*
368 		 * Upon RESUME, the graphics device will always actually
369 		 * be in TEXT mode even though the Xorg server did not
370 		 * make that mode change itself (the suspend code did).
371 		 * We want first, therefore, to restore textmode
372 		 * operation fully, and then the Xorg server will
373 		 * do the rest to restore the device to its
374 		 * (hi resolution) graphics mode
375 		 */
376 		vgatext_kdsettext(softc);
377 #if	defined(USE_BORDERS)
378 		vgatext_init_graphics(softc);
379 #endif
380 		break;
381 	default:
382 		cmn_err(CE_WARN, MYNAME ": unknown mode in vgatext_resume.");
383 		break;
384 	}
385 }
386 
387 static void
388 vgatext_progressbar_stop(struct gfxp_fb_softc *softc)
389 {
390 	extern void progressbar_stop(void);
391 
392 	if (softc->silent == 1) {
393 		softc->silent = 0;
394 		progressbar_stop();
395 	}
396 }
397 
398 static void
399 vgatext_kdsettext(struct gfxp_fb_softc *softc)
400 {
401 	union gfx_console *console = softc->console;
402 	int i;
403 
404 	vgatext_init(softc);
405 	for (i = 0; i < sizeof (console->vga.shadow); i++) {
406 		console->vga.text_base[i] = console->vga.shadow[i];
407 	}
408 	console->vga.current_base = console->vga.text_base;
409 	if (console->vga.cursor.visible) {
410 		vgatext_set_cursor(softc,
411 		    console->vga.cursor.row,
412 		    console->vga.cursor.col);
413 	}
414 	vgatext_restore_colormap(softc);
415 }
416 
417 static void
418 vgatext_kdsetgraphics(struct gfxp_fb_softc *softc)
419 {
420 	vgatext_progressbar_stop(softc);
421 	vgatext_save_text(softc);
422 	softc->console->vga.current_base = softc->console->vga.shadow;
423 	vgatext_get_text(softc);
424 #if	defined(USE_BORDERS)
425 	vgatext_init_graphics(softc);
426 #endif
427 }
428 
429 static int
430 vgatext_kdsetmode(struct gfxp_fb_softc *softc, int mode)
431 {
432 	switch (mode) {
433 	case KD_TEXT:
434 		if (softc->blt_ops.setmode != NULL)
435 			softc->blt_ops.setmode(KD_TEXT);
436 		vgatext_kdsettext(softc);
437 		break;
438 
439 	case KD_GRAPHICS:
440 		vgatext_kdsetgraphics(softc);
441 		if (softc->blt_ops.setmode != NULL)
442 			softc->blt_ops.setmode(KD_GRAPHICS);
443 		break;
444 
445 	case KD_RESETTEXT:
446 		/*
447 		 * In order to avoid racing with a starting X server,
448 		 * this needs to be a test and set that is performed in
449 		 * a single (softc->lock protected) ioctl into this driver.
450 		 */
451 		if (softc->mode == KD_TEXT && softc->silent == 1) {
452 			vgatext_progressbar_stop(softc);
453 			vgatext_kdsettext(softc);
454 		}
455 		mode = KD_TEXT;
456 		break;
457 
458 	default:
459 		return (EINVAL);
460 	}
461 
462 	softc->mode = mode;
463 	return (0);
464 }
465 
466 /*ARGSUSED*/
467 static int
468 vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
469     size_t *maplen, uint_t model, void *ptr)
470 {
471 	struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr;
472 	union gfx_console *console;
473 	int err;
474 	size_t length;
475 
476 
477 	if (softc == NULL) {
478 		cmn_err(CE_WARN, "vgatext: Can't find softstate");
479 		return (-1);
480 	}
481 	console = softc->console;
482 
483 	if (!(off >= VGA_MEM_ADDR &&
484 	    off < VGA_MEM_ADDR + console->vga.fb_size)) {
485 		cmn_err(CE_WARN, "vgatext: Can't map offset 0x%llx", off);
486 		return (-1);
487 	}
488 
489 	if (off + len > VGA_MEM_ADDR + console->vga.fb_size)
490 		length = VGA_MEM_ADDR + console->vga.fb_size - off;
491 	else
492 		length = len;
493 
494 	if ((err = devmap_devmem_setup(dhp, softc->devi,
495 	    NULL, console->vga.fb_regno, off - VGA_MEM_ADDR,
496 	    length, PROT_ALL, 0, &dev_attr)) < 0) {
497 		return (err);
498 	}
499 
500 
501 	*maplen = length;
502 	return (0);
503 }
504 
505 
506 static int
507 vgatext_devinit(struct gfxp_fb_softc *softc, struct vis_devinit *data)
508 {
509 	/* initialize console instance */
510 	data->version = VIS_CONS_REV;
511 	data->width = VGA_TEXT_COLS;
512 	data->height = VGA_TEXT_ROWS;
513 	data->linebytes = VGA_TEXT_COLS;
514 	data->color_map = NULL;
515 	data->depth = 4;
516 	data->mode = VIS_TEXT;
517 	data->polledio = &softc->polledio;
518 
519 	vgatext_save_text(softc);	/* save current console */
520 	vgatext_hide_cursor(softc);
521 	return (0);
522 }
523 
524 /*
525  * display a string on the screen at (row, col)
526  *	 assume it has been cropped to fit.
527  */
528 
529 static void
530 vgatext_cons_display(struct gfxp_fb_softc *softc, struct vis_consdisplay *da)
531 {
532 	unsigned char	*string;
533 	int	i;
534 	unsigned char	attr;
535 	struct cgatext {
536 		unsigned char ch;
537 		unsigned char attr;
538 	};
539 	struct cgatext *addr;
540 
541 	/*
542 	 * Sanity checks.  This is a last-ditch effort to avoid damage
543 	 * from brokenness or maliciousness above.
544 	 */
545 	if (da->row < 0 || da->row >= VGA_TEXT_ROWS ||
546 	    da->col < 0 || da->col >= VGA_TEXT_COLS ||
547 	    da->col + da->width > VGA_TEXT_COLS)
548 		return;
549 
550 	/*
551 	 * To be fully general, we should copyin the data.  This is not
552 	 * really relevant for this text-only driver, but a graphical driver
553 	 * should support these ioctls from userland to enable simple
554 	 * system startup graphics.
555 	 */
556 	attr = (solaris_color_to_pc_color[da->bg_color & 0xf] << 4)
557 	    | solaris_color_to_pc_color[da->fg_color & 0xf];
558 	string = da->data;
559 	addr = (struct cgatext *)softc->console->vga.current_base
560 	    +  (da->row * VGA_TEXT_COLS + da->col);
561 	for (i = 0; i < da->width; i++) {
562 		addr->ch = string[i];
563 		addr->attr = attr;
564 		addr++;
565 	}
566 }
567 
568 static void
569 vgatext_polled_display(
570 	struct vis_polledio_arg *arg,
571 	struct vis_consdisplay *da)
572 {
573 	vgatext_cons_display((struct gfxp_fb_softc *)arg, da);
574 }
575 
576 /*
577  * screen-to-screen copy
578  */
579 
580 static void
581 vgatext_cons_copy(struct gfxp_fb_softc *softc, struct vis_conscopy *ma)
582 {
583 	unsigned short	*from;
584 	unsigned short	*to;
585 	int		cnt;
586 	screen_size_t chars_per_row;
587 	unsigned short	*to_row_start;
588 	unsigned short	*from_row_start;
589 	screen_size_t	rows_to_move;
590 	unsigned short	*base;
591 
592 	/*
593 	 * Sanity checks.  Note that this is a last-ditch effort to avoid
594 	 * damage caused by broken-ness or maliciousness above.
595 	 */
596 	if (ma->s_col < 0 || ma->s_col >= VGA_TEXT_COLS ||
597 	    ma->s_row < 0 || ma->s_row >= VGA_TEXT_ROWS ||
598 	    ma->e_col < 0 || ma->e_col >= VGA_TEXT_COLS ||
599 	    ma->e_row < 0 || ma->e_row >= VGA_TEXT_ROWS ||
600 	    ma->t_col < 0 || ma->t_col >= VGA_TEXT_COLS ||
601 	    ma->t_row < 0 || ma->t_row >= VGA_TEXT_ROWS ||
602 	    ma->s_col > ma->e_col ||
603 	    ma->s_row > ma->e_row)
604 		return;
605 
606 	/*
607 	 * Remember we're going to copy shorts because each
608 	 * character/attribute pair is 16 bits.
609 	 */
610 	chars_per_row = ma->e_col - ma->s_col + 1;
611 	rows_to_move = ma->e_row - ma->s_row + 1;
612 
613 	/* More sanity checks. */
614 	if (ma->t_row + rows_to_move > VGA_TEXT_ROWS ||
615 	    ma->t_col + chars_per_row > VGA_TEXT_COLS)
616 		return;
617 
618 	base = (unsigned short *)softc->console->vga.current_base;
619 
620 	to_row_start = base + ((ma->t_row * VGA_TEXT_COLS) + ma->t_col);
621 	from_row_start = base + ((ma->s_row * VGA_TEXT_COLS) + ma->s_col);
622 
623 	if (to_row_start < from_row_start) {
624 		while (rows_to_move-- > 0) {
625 			to = to_row_start;
626 			from = from_row_start;
627 			to_row_start += VGA_TEXT_COLS;
628 			from_row_start += VGA_TEXT_COLS;
629 			for (cnt = chars_per_row; cnt-- > 0; )
630 				*to++ = *from++;
631 		}
632 	} else {
633 		/*
634 		 * Offset to the end of the region and copy backwards.
635 		 */
636 		cnt = rows_to_move * VGA_TEXT_COLS + chars_per_row;
637 		to_row_start += cnt;
638 		from_row_start += cnt;
639 
640 		while (rows_to_move-- > 0) {
641 			to_row_start -= VGA_TEXT_COLS;
642 			from_row_start -= VGA_TEXT_COLS;
643 			to = to_row_start;
644 			from = from_row_start;
645 			for (cnt = chars_per_row; cnt-- > 0; )
646 				*--to = *--from;
647 		}
648 	}
649 }
650 
651 static void
652 vgatext_polled_copy(
653 	struct vis_polledio_arg *arg,
654 	struct vis_conscopy *ca)
655 {
656 	vgatext_cons_copy((struct gfxp_fb_softc *)arg, ca);
657 }
658 
659 /*ARGSUSED*/
660 static int
661 vgatext_cons_clear(struct gfxp_fb_softc *softc, struct vis_consclear *ca)
662 {
663 	uint16_t val, fg, *base;
664 	int i;
665 
666 	if (ca->bg_color == 0)		/* bright white */
667 		fg = 1;			/* black */
668 	else
669 		fg = 8;
670 
671 	val = (solaris_color_to_pc_color[ca->bg_color & 0xf] << 4) |
672 	    solaris_color_to_pc_color[fg];
673 	val = (val << 8) | ' ';
674 
675 	base = (uint16_t *)softc->console->vga.current_base;
676 	for (i = 0; i < VGA_TEXT_ROWS * VGA_TEXT_COLS; i++)
677 		base[i] = val;
678 
679 	return (0);
680 }
681 
682 static void
683 vgatext_cons_cursor(struct gfxp_fb_softc *softc, struct vis_conscursor *ca)
684 {
685 	if (softc->silent)
686 		return;
687 
688 	switch (ca->action) {
689 	case VIS_HIDE_CURSOR:
690 		softc->console->vga.cursor.visible = B_FALSE;
691 		if (softc->console->vga.current_base ==
692 		    softc->console->vga.text_base)
693 			vgatext_hide_cursor(softc);
694 		break;
695 	case VIS_DISPLAY_CURSOR:
696 		/*
697 		 * Sanity check.  This is a last-ditch effort to avoid
698 		 * damage from brokenness or maliciousness above.
699 		 */
700 		if (ca->col < 0 || ca->col >= VGA_TEXT_COLS ||
701 		    ca->row < 0 || ca->row >= VGA_TEXT_ROWS)
702 			return;
703 
704 		softc->console->vga.cursor.visible = B_TRUE;
705 		softc->console->vga.cursor.col = ca->col;
706 		softc->console->vga.cursor.row = ca->row;
707 		if (softc->console->vga.current_base ==
708 		    softc->console->vga.text_base)
709 			vgatext_set_cursor(softc, ca->row, ca->col);
710 		break;
711 	case VIS_GET_CURSOR:
712 		if (softc->console->vga.current_base ==
713 		    softc->console->vga.text_base) {
714 			vgatext_get_cursor(softc, &ca->row, &ca->col);
715 		}
716 		break;
717 	}
718 }
719 
720 static void
721 vgatext_polled_cursor(
722 	struct vis_polledio_arg *arg,
723 	struct vis_conscursor *ca)
724 {
725 	vgatext_cons_cursor((struct gfxp_fb_softc *)arg, ca);
726 }
727 
728 static void
729 vgatext_hide_cursor(struct gfxp_fb_softc *softc)
730 {
731 	union gfx_console *console = softc->console;
732 	uint8_t msl, s;
733 
734 	if (softc->silent)
735 		return;
736 
737 	msl = vga_get_crtc(&console->vga.regs, VGA_CRTC_MAX_S_LN) & 0x1f;
738 	s = vga_get_crtc(&console->vga.regs, VGA_CRTC_CSSL) & 0xc0;
739 	s |= (1 << 5);
740 
741 	/* disable cursor */
742 	vga_set_crtc(&console->vga.regs, VGA_CRTC_CSSL, s);
743 	vga_set_crtc(&console->vga.regs, VGA_CRTC_CESL, msl);
744 }
745 
746 static void
747 vgatext_set_cursor(struct gfxp_fb_softc *softc, int row, int col)
748 {
749 	union gfx_console *console = softc->console;
750 	short	addr;
751 	uint8_t msl, s;
752 
753 	if (softc->silent)
754 		return;
755 
756 	msl = vga_get_crtc(&console->vga.regs, VGA_CRTC_MAX_S_LN) & 0x1f;
757 	s = vga_get_crtc(&console->vga.regs, VGA_CRTC_CSSL) & 0xc0;
758 
759 	addr = row * VGA_TEXT_COLS + col;
760 
761 	vga_set_crtc(&console->vga.regs, VGA_CRTC_CLAH, addr >> 8);
762 	vga_set_crtc(&console->vga.regs, VGA_CRTC_CLAL, addr & 0xff);
763 
764 	/* enable cursor */
765 	vga_set_crtc(&console->vga.regs, VGA_CRTC_CSSL, s);
766 	vga_set_crtc(&console->vga.regs, VGA_CRTC_CESL, msl);
767 }
768 
769 static void
770 vgatext_get_cursor(struct gfxp_fb_softc *softc,
771     screen_pos_t *row, screen_pos_t *col)
772 {
773 	union gfx_console *console = softc->console;
774 	short   addr;
775 
776 	addr = (vga_get_crtc(&console->vga.regs, VGA_CRTC_CLAH) << 8) +
777 	    vga_get_crtc(&console->vga.regs, VGA_CRTC_CLAL);
778 
779 	*row = addr / VGA_TEXT_COLS;
780 	*col = addr % VGA_TEXT_COLS;
781 }
782 
783 static void
784 vgatext_get_text(struct gfxp_fb_softc *softc)
785 {
786 	union gfx_console *console = softc->console;
787 	struct vgareg *vga_reg;
788 	struct vgaregmap *regs;
789 	int i;
790 
791 	regs = &console->vga.regs;
792 	vga_reg = &console->vga.vga_reg;
793 
794 	vga_reg->vga_misc = vga_get_reg(regs, VGA_MISC_R);
795 
796 	/* get crt controller registers */
797 	for (i = 0; i < NUM_CRTC_REG; i++) {
798 		vga_reg->vga_crtc[i] = vga_get_crtc(regs, i);
799 	}
800 
801 	/* get attribute registers */
802 	for (i = 0; i < NUM_ATR_REG; i++) {
803 		vga_reg->vga_atr[i] = vga_get_atr(regs, i);
804 	}
805 
806 	/* get graphics controller registers */
807 	for (i = 0; i < NUM_GRC_REG; i++) {
808 		vga_reg->vga_grc[i] = vga_get_grc(regs, i);
809 	}
810 
811 	/* get sequencer registers */
812 	for (i = 1; i < NUM_SEQ_REG; i++) {
813 		vga_reg->vga_seq[i] = vga_get_seq(regs, i);
814 	}
815 }
816 
817 /*
818  * This code is experimental. It's only enabled if console is
819  * set to graphics, a preliminary implementation of happyface boot.
820  */
821 static void
822 vgatext_set_text(struct gfxp_fb_softc *softc)
823 {
824 	union gfx_console *console = softc->console;
825 	struct vgareg *vga_reg;
826 	struct vgaregmap *regs;
827 	int i;
828 
829 	regs = &console->vga.regs;
830 	vga_reg = &console->vga.vga_reg;
831 
832 	vgatext_get_text(softc);
833 
834 	/*
835 	 * Set output register bits for text mode.
836 	 * Make sure the VGA adapter is not in monochrome emulation mode.
837 	 */
838 	vga_set_reg(regs, VGA_MISC_W, VGA_MISC_HSP | VGA_MISC_PGSL |
839 	    VGA_MISC_VCLK1 | VGA_MISC_ENB_RAM | VGA_MISC_IOA_SEL);
840 
841 	/* set sequencer registers */
842 	vga_set_seq(regs, VGA_SEQ_RST_SYN,
843 	    (vga_get_seq(regs, VGA_SEQ_RST_SYN) &
844 	    ~VGA_SEQ_RST_SYN_NO_SYNC_RESET));
845 	for (i = 1; i < NUM_SEQ_REG; i++) {
846 		vga_set_seq(regs, i, VGA_SEQ_TEXT[i]);
847 	}
848 	vga_set_seq(regs, VGA_SEQ_RST_SYN,
849 	    (vga_get_seq(regs, VGA_SEQ_RST_SYN) |
850 	    VGA_SEQ_RST_SYN_NO_ASYNC_RESET |
851 	    VGA_SEQ_RST_SYN_NO_SYNC_RESET));
852 
853 	/* set crt controller registers */
854 	vga_set_crtc(regs, VGA_CRTC_VRE,
855 	    (vga_reg->vga_crtc[VGA_CRTC_VRE] & ~VGA_CRTC_VRE_LOCK));
856 	for (i = 0; i < NUM_CRTC_REG; i++) {
857 		vga_set_crtc(regs, i, VGA_CRTC_TEXT[i]);
858 	}
859 
860 	/* set graphics controller registers */
861 	for (i = 0; i < NUM_GRC_REG; i++) {
862 		vga_set_grc(regs, i, VGA_GRC_TEXT[i]);
863 	}
864 
865 	/* set attribute registers */
866 	for (i = 0; i < NUM_ATR_REG; i++) {
867 		vga_set_atr(regs, i, VGA_ATR_TEXT[i]);
868 	}
869 
870 	/* set palette */
871 	for (i = 0; i < VGA_TEXT_CMAP_ENTRIES; i++) {
872 		vga_put_cmap(regs, i,
873 		    VGA_TEXT_PALETTES[i][0] << 2,
874 		    VGA_TEXT_PALETTES[i][1] << 2,
875 		    VGA_TEXT_PALETTES[i][2] << 2);
876 	}
877 	for (i = VGA_TEXT_CMAP_ENTRIES; i < VGA8_CMAP_ENTRIES; i++) {
878 		vga_put_cmap(regs, i, 0, 0, 0);
879 	}
880 }
881 
882 static void
883 vgatext_init(struct gfxp_fb_softc *softc)
884 {
885 	union gfx_console *console = softc->console;
886 	unsigned char atr_mode;
887 
888 	atr_mode = vga_get_atr(&console->vga.regs, VGA_ATR_MODE);
889 	if (atr_mode & VGA_ATR_MODE_GRAPH)
890 		vgatext_set_text(softc);
891 	atr_mode = vga_get_atr(&console->vga.regs, VGA_ATR_MODE);
892 	atr_mode &= ~VGA_ATR_MODE_BLINK;
893 	atr_mode &= ~VGA_ATR_MODE_9WIDE;
894 	vga_set_atr(&console->vga.regs, VGA_ATR_MODE, atr_mode);
895 #if	defined(USE_BORDERS)
896 	vga_set_atr(&console->vga.regs, VGA_ATR_BDR_CLR,
897 	    vga_get_atr(&console->vga.regs, pc_brt_white));
898 #else
899 	vga_set_atr(&console->vga.regs, VGA_ATR_BDR_CLR,
900 	    vga_get_atr(&console->vga.regs, pc_black));
901 #endif
902 	vgatext_setfont(softc);	/* need selectable font? */
903 }
904 
905 #if	defined(USE_BORDERS)
906 static void
907 vgatext_init_graphics(struct gfxp_fb_softc *softc)
908 {
909 	vga_set_atr(&softc->console->vga.regs, VGA_ATR_BDR_CLR,
910 	    vga_get_atr(&softc->console->vga.regs, VGA_BLACK));
911 }
912 #endif
913 
914 static void
915 vgatext_setfont(struct gfxp_fb_softc *softc)
916 {
917 	union gfx_console *console = softc->console;
918 	static uchar_t fsreg[8] = {0x0, 0x30, 0x5, 0x35, 0xa, 0x3a, 0xf, 0x3f};
919 
920 	uchar_t *from;
921 	uchar_t volatile *to;
922 	int	i, j, s;
923 	int	bpc, f_offset;
924 
925 	/* Sync-reset the sequencer registers */
926 	vga_set_seq(&console->vga.regs, 0x00, 0x01);
927 	/*
928 	 *  enable write to plane2, since fonts
929 	 * could only be loaded into plane2
930 	 */
931 	vga_set_seq(&console->vga.regs, 0x02, 0x04);
932 	/*
933 	 *  sequentially access data in the bit map being
934 	 * selected by MapMask register (index 0x02)
935 	 */
936 	vga_set_seq(&console->vga.regs, 0x04, 0x07);
937 	/* Sync-reset ended, and allow the sequencer to operate */
938 	vga_set_seq(&console->vga.regs, 0x00, 0x03);
939 
940 	/*
941 	 *  select plane 2 on Read Mode 0
942 	 */
943 	vga_set_grc(&console->vga.regs, 0x04, 0x02);
944 	/*
945 	 *  system addresses sequentially access data, follow
946 	 * Memory Mode register bit 2 in the sequencer
947 	 */
948 	vga_set_grc(&console->vga.regs, 0x05, 0x00);
949 	/*
950 	 * set range of host memory addresses decoded by VGA
951 	 * hardware -- A0000h-BFFFFh (128K region)
952 	 */
953 	vga_set_grc(&console->vga.regs, 0x06, 0x00);
954 
955 	/*
956 	 * This assumes 8x16 characters, which yield the traditional 80x25
957 	 * screen.  It really should support other character heights.
958 	 */
959 	bpc = 16;
960 	s = console->vga.vga_fontslot;
961 	f_offset = s * 8 * 1024;
962 	for (i = 0; i < 256; i++) {
963 		from = font_data_8x16.encoding[i];
964 		to = (unsigned char *)console->vga.fb.addr + f_offset +
965 		    i * 0x20;
966 		for (j = 0; j < bpc; j++)
967 			*to++ = *from++;
968 	}
969 
970 	/* Sync-reset the sequencer registers */
971 	vga_set_seq(&console->vga.regs, 0x00, 0x01);
972 	/* enable write to plane 0 and 1 */
973 	vga_set_seq(&console->vga.regs, 0x02, 0x03);
974 	/*
975 	 * enable character map selection
976 	 * and odd/even addressing
977 	 */
978 	vga_set_seq(&console->vga.regs, 0x04, 0x03);
979 	/*
980 	 * select font map
981 	 */
982 	vga_set_seq(&console->vga.regs, 0x03, fsreg[s]);
983 	/* Sync-reset ended, and allow the sequencer to operate */
984 	vga_set_seq(&console->vga.regs, 0x00, 0x03);
985 
986 	/* restore graphic registers */
987 
988 	/* select plane 0 */
989 	vga_set_grc(&console->vga.regs, 0x04, 0x00);
990 	/* enable odd/even addressing mode */
991 	vga_set_grc(&console->vga.regs, 0x05, 0x10);
992 	/*
993 	 * range of host memory addresses decoded by VGA
994 	 * hardware -- B8000h-BFFFFh (32K region)
995 	 */
996 	vga_set_grc(&console->vga.regs, 0x06, 0x0e);
997 	/* enable all color plane */
998 	vga_set_atr(&console->vga.regs, 0x12, 0x0f);
999 
1000 }
1001 
1002 static void
1003 vgatext_save_colormap(struct gfxp_fb_softc *softc)
1004 {
1005 	union gfx_console *console = softc->console;
1006 	int i;
1007 
1008 	for (i = 0; i < VGA_ATR_NUM_PLT; i++) {
1009 		console->vga.attrib_palette[i] =
1010 		    vga_get_atr(&console->vga.regs, i);
1011 	}
1012 	for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
1013 		vga_get_cmap(&console->vga.regs, i,
1014 		    &console->vga.colormap[i].red,
1015 		    &console->vga.colormap[i].green,
1016 		    &console->vga.colormap[i].blue);
1017 	}
1018 }
1019 
1020 static void
1021 vgatext_restore_colormap(struct gfxp_fb_softc *softc)
1022 {
1023 	union gfx_console *console = softc->console;
1024 	int i;
1025 
1026 	for (i = 0; i < VGA_ATR_NUM_PLT; i++) {
1027 		vga_set_atr(&console->vga.regs, i,
1028 		    console->vga.attrib_palette[i]);
1029 	}
1030 	for (i = 0; i < VGA8_CMAP_ENTRIES; i++) {
1031 		vga_put_cmap(&console->vga.regs, i,
1032 		    console->vga.colormap[i].red,
1033 		    console->vga.colormap[i].green,
1034 		    console->vga.colormap[i].blue);
1035 	}
1036 }
1037 
1038 /*
1039  * search the entries of the "reg" property for one which has the desired
1040  * combination of phys_hi bits and contains the desired address.
1041  *
1042  * This version searches a PCI-style "reg" property.  It was prompted by
1043  * issues surrounding the presence or absence of an entry for the ROM:
1044  * (a) a transition problem with PowerPC Virtual Open Firmware
1045  * (b) uncertainty as to whether an entry will be included on a device
1046  *     with ROM support (and so an "active" ROM base address register),
1047  *     but no ROM actually installed.
1048  *
1049  * See the note below on vgatext_get_isa_reg_index for the reasons for
1050  * returning the offset.
1051  *
1052  * Note that this routine may not be fully general; it is intended for the
1053  * specific purpose of finding a couple of particular VGA reg entries and
1054  * may not be suitable for all reg-searching purposes.
1055  */
1056 static int
1057 vgatext_get_pci_reg_index(
1058 	dev_info_t *const devi,
1059 	unsigned long himask,
1060 	unsigned long hival,
1061 	unsigned long addr,
1062 	off_t *offset)
1063 {
1064 
1065 	int			length, index;
1066 	pci_regspec_t	*reg;
1067 
1068 	if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
1069 	    "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
1070 		return (-1);
1071 	}
1072 
1073 	for (index = 0; index < length / sizeof (pci_regspec_t); index++) {
1074 		if ((reg[index].pci_phys_hi & himask) != hival)
1075 			continue;
1076 		if (reg[index].pci_size_hi != 0)
1077 			continue;
1078 		if (reg[index].pci_phys_mid != 0)
1079 			continue;
1080 		if (reg[index].pci_phys_low > addr)
1081 			continue;
1082 		if (reg[index].pci_phys_low + reg[index].pci_size_low <= addr)
1083 			continue;
1084 
1085 		*offset = addr - reg[index].pci_phys_low;
1086 		kmem_free(reg, (size_t)length);
1087 		return (index);
1088 	}
1089 	kmem_free(reg, (size_t)length);
1090 
1091 	return (-1);
1092 }
1093 
1094 /*
1095  * search the entries of the "reg" property for one which has the desired
1096  * combination of phys_hi bits and contains the desired address.
1097  *
1098  * This version searches a ISA-style "reg" property.  It was prompted by
1099  * issues surrounding 8514/A support.  By IEEE 1275 compatibility conventions,
1100  * 8514/A registers should have been added after all standard VGA registers.
1101  * Unfortunately, the Solaris/Intel device configuration framework
1102  * (a) lists the 8514/A registers before the video memory, and then
1103  * (b) also sorts the entries so that I/O entries come before memory
1104  *     entries.
1105  *
1106  * It returns the "reg" index and offset into that register set.
1107  * The offset is needed because there exist (broken?) BIOSes that
1108  * report larger ranges enclosing the standard ranges.  One reports
1109  * 0x3bf for 0x21 instead of 0x3c0 for 0x20, for instance.  Using the
1110  * offset adjusts for this difference in the base of the register set.
1111  *
1112  * Note that this routine may not be fully general; it is intended for the
1113  * specific purpose of finding a couple of particular VGA reg entries and
1114  * may not be suitable for all reg-searching purposes.
1115  */
1116 static int
1117 vgatext_get_isa_reg_index(
1118 	dev_info_t *const devi,
1119 	unsigned long hival,
1120 	unsigned long addr,
1121 	off_t *offset)
1122 {
1123 
1124 	int		length, index;
1125 	struct regspec	*reg;
1126 
1127 	if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
1128 	    "reg", (caddr_t)&reg, &length) != DDI_PROP_SUCCESS) {
1129 		return (-1);
1130 	}
1131 
1132 	for (index = 0; index < length / sizeof (struct regspec); index++) {
1133 		if (reg[index].regspec_bustype != hival)
1134 			continue;
1135 		if (reg[index].regspec_addr > addr)
1136 			continue;
1137 		if (reg[index].regspec_addr + reg[index].regspec_size <= addr)
1138 			continue;
1139 
1140 		*offset = addr - reg[index].regspec_addr;
1141 		kmem_free(reg, (size_t)length);
1142 		return (index);
1143 	}
1144 	kmem_free(reg, (size_t)length);
1145 
1146 	return (-1);
1147 }
1148 
1149 /*
1150  * This vgatext function is used to return the fb, and reg pointers
1151  * and handles for peer graphics drivers.
1152  */
1153 
1154 void
1155 vgatext_return_pointers(struct gfxp_fb_softc *softc, struct vgaregmap *fbs,
1156     struct vgaregmap *regss)
1157 {
1158 
1159 	fbs->addr	= softc->console->vga.fb.addr;
1160 	fbs->handle	= softc->console->vga.fb.handle;
1161 	fbs->mapped	= softc->console->vga.fb.mapped;
1162 	regss->addr	= softc->console->vga.regs.addr;
1163 	regss->handle	= softc->console->vga.regs.handle;
1164 	regss->mapped	= softc->console->vga.regs.mapped;
1165 }
1166