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 2009 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/file.h> 42 #include <sys/open.h> 43 #include <sys/modctl.h> 44 #include <sys/vgareg.h> 45 #include <sys/vgasubr.h> 46 #include <sys/pci.h> 47 #include <sys/kd.h> 48 #include <sys/ddi_impldefs.h> 49 50 #include "gfx_private.h" 51 52 #define MYNAME "gfxp_vgatext" 53 54 #define TEXT_ROWS 25 55 #define TEXT_COLS 80 56 57 #define VGA_BRIGHT_WHITE 0x0f 58 #define VGA_BLACK 0x00 59 60 #define VGA_REG_ADDR 0x3c0 61 #define VGA_REG_SIZE 0x20 62 63 #define VGA_MEM_ADDR 0xa0000 64 #define VGA_MEM_SIZE 0x20000 65 66 #define VGA_MMAP_FB_BASE VGA_MEM_ADDR 67 68 struct vgatext_softc { 69 struct vgaregmap regs; 70 struct vgaregmap fb; 71 off_t fb_size; 72 int fb_regno; 73 dev_info_t *devi; 74 int mode; /* KD_TEXT or KD_GRAPHICS */ 75 caddr_t text_base; /* hardware text base */ 76 char shadow[TEXT_ROWS*TEXT_COLS*2]; 77 caddr_t current_base; /* hardware or shadow */ 78 struct { 79 boolean_t visible; 80 int row; 81 int col; 82 } cursor; 83 struct vis_polledio polledio; 84 struct { 85 unsigned char red; 86 unsigned char green; 87 unsigned char blue; 88 } colormap[VGA8_CMAP_ENTRIES]; 89 unsigned char attrib_palette[VGA_ATR_NUM_PLT]; 90 unsigned int flags; 91 kmutex_t lock; 92 }; 93 94 typedef enum pc_colors { 95 pc_black = 0, 96 pc_blue = 1, 97 pc_green = 2, 98 pc_cyan = 3, 99 pc_red = 4, 100 pc_magenta = 5, 101 pc_brown = 6, 102 pc_white = 7, 103 pc_grey = 8, 104 pc_brt_blue = 9, 105 pc_brt_green = 10, 106 pc_brt_cyan = 11, 107 pc_brt_red = 12, 108 pc_brt_magenta = 13, 109 pc_yellow = 14, 110 pc_brt_white = 15 111 } pc_colors_t; 112 113 static const unsigned char solaris_color_to_pc_color[16] = { 114 pc_brt_white, /* 0 - brt_white */ 115 pc_black, /* 1 - black */ 116 pc_blue, /* 2 - blue */ 117 pc_green, /* 3 - green */ 118 pc_cyan, /* 4 - cyan */ 119 pc_red, /* 5 - red */ 120 pc_magenta, /* 6 - magenta */ 121 pc_brown, /* 7 - brown */ 122 pc_white, /* 8 - white */ 123 pc_grey, /* 9 - gery */ 124 pc_brt_blue, /* 10 - brt_blue */ 125 pc_brt_green, /* 11 - brt_green */ 126 pc_brt_cyan, /* 12 - brt_cyan */ 127 pc_brt_red, /* 13 - brt_red */ 128 pc_brt_magenta, /* 14 - brt_magenta */ 129 pc_yellow /* 15 - yellow */ 130 }; 131 132 static ddi_device_acc_attr_t dev_attr = { 133 DDI_DEVICE_ATTR_V0, 134 DDI_NEVERSWAP_ACC, 135 DDI_STRICTORDER_ACC, 136 }; 137 138 /* default structure for FBIOGATTR ioctl */ 139 static struct fbgattr vgatext_attr = { 140 /* real_type owner */ 141 FBTYPE_SUNFAST_COLOR, 0, 142 /* fbtype: type h w depth cms size */ 143 { FBTYPE_SUNFAST_COLOR, TEXT_ROWS, TEXT_COLS, 1, 256, 0 }, 144 /* fbsattr: flags emu_type dev_specific */ 145 { 0, FBTYPE_SUN4COLOR, { 0 } }, 146 /* emu_types */ 147 { -1 } 148 }; 149 150 #define GFXP_FLAG_CONSOLE 0x00000001 151 #define GFXP_IS_CONSOLE(softc) ((softc)->flags & GFXP_FLAG_CONSOLE) 152 153 /* 154 * Global name used to write the softc pointer in, for the 155 * data wrapper vgatext_return_pointers() 156 */ 157 158 159 int gfxp_vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd, 160 gfxp_vgatext_softc_ptr_t ptr); 161 static int vgatext_devinit(struct vgatext_softc *, struct vis_devinit *data); 162 static void vgatext_cons_copy(struct vgatext_softc *, 163 struct vis_conscopy *); 164 static void vgatext_cons_display(struct vgatext_softc *, 165 struct vis_consdisplay *); 166 static void vgatext_cons_cursor(struct vgatext_softc *, 167 struct vis_conscursor *); 168 static void vgatext_polled_copy(struct vis_polledio_arg *, 169 struct vis_conscopy *); 170 static void vgatext_polled_display(struct vis_polledio_arg *, 171 struct vis_consdisplay *); 172 static void vgatext_polled_cursor(struct vis_polledio_arg *, 173 struct vis_conscursor *); 174 static void vgatext_init(struct vgatext_softc *); 175 static void vgatext_set_text(struct vgatext_softc *); 176 177 static void vgatext_save_text(struct vgatext_softc *softc); 178 static void vgatext_restore_textmode(struct vgatext_softc *softc); 179 static int vgatext_suspend(struct vgatext_softc *softc); 180 static void vgatext_resume(struct vgatext_softc *softc); 181 182 #if defined(USE_BORDERS) 183 static void vgatext_init_graphics(struct vgatext_softc *); 184 #endif 185 186 static int vgatext_kdsetmode(struct vgatext_softc *softc, int mode); 187 static void vgatext_setfont(struct vgatext_softc *softc); 188 static void vgatext_get_cursor(struct vgatext_softc *softc, 189 screen_pos_t *row, screen_pos_t *col); 190 static void vgatext_set_cursor(struct vgatext_softc *softc, int row, int col); 191 static void vgatext_hide_cursor(struct vgatext_softc *softc); 192 static void vgatext_save_colormap(struct vgatext_softc *softc); 193 static void vgatext_restore_colormap(struct vgatext_softc *softc); 194 static int vgatext_get_pci_reg_index(dev_info_t *const devi, 195 unsigned long himask, unsigned long hival, unsigned long addr, 196 off_t *offset); 197 static int vgatext_get_isa_reg_index(dev_info_t *const devi, 198 unsigned long hival, unsigned long addr, off_t *offset); 199 200 static char vgatext_silent; 201 static char happyface_boot; 202 203 gfxp_vgatext_softc_ptr_t 204 gfxp_vgatext_softc_alloc(void) 205 { 206 return (kmem_zalloc(sizeof (struct vgatext_softc), KM_SLEEP)); 207 } 208 209 void 210 gfxp_vgatext_softc_free(gfxp_vgatext_softc_ptr_t ptr) 211 { 212 kmem_free(ptr, sizeof (struct vgatext_softc)); 213 } 214 215 #define STREQ(a, b) (strcmp((a), (b)) == 0) 216 217 static void 218 gfxp_check_for_console(dev_info_t *devi, struct vgatext_softc *softc, 219 int pci_pcie_bus) 220 { 221 ddi_acc_handle_t pci_conf; 222 dev_info_t *pdevi; 223 uint16_t data16; 224 225 /* 226 * Based on Section 11.3, "PCI Display Subsystem Initialization", 227 * of the 1.1 PCI-to-PCI Bridge Architecture Specification 228 * determine if this is the boot console device. First, see 229 * if the SBIOS has turned on PCI I/O for this device. Then if 230 * this is PCI/PCI-E, verify the parent bridge has VGAEnable set. 231 */ 232 233 if (pci_config_setup(devi, &pci_conf) != DDI_SUCCESS) { 234 cmn_err(CE_WARN, 235 MYNAME 236 ": can't get PCI conf handle"); 237 return; 238 } 239 240 data16 = pci_config_get16(pci_conf, PCI_CONF_COMM); 241 if (data16 & PCI_COMM_IO) 242 softc->flags |= GFXP_FLAG_CONSOLE; 243 244 pci_config_teardown(&pci_conf); 245 246 /* If IO not enabled or ISA/EISA, just return */ 247 if (!(softc->flags & GFXP_FLAG_CONSOLE) || !pci_pcie_bus) 248 return; 249 250 /* 251 * Check for VGA Enable in the Bridge Control register for all 252 * PCI/PCIEX parents. If not set all the way up the chain, 253 * this cannot be the boot console. 254 */ 255 256 pdevi = ddi_get_parent(devi); 257 while (pdevi) { 258 int error; 259 ddi_acc_handle_t ppci_conf; 260 char *parent_type = NULL; 261 262 error = ddi_prop_lookup_string(DDI_DEV_T_ANY, pdevi, 263 DDI_PROP_DONTPASS, "device_type", &parent_type); 264 if (error != DDI_SUCCESS) { 265 return; 266 } 267 268 /* Verify still on the PCI/PCIEX parent tree */ 269 if (!STREQ(parent_type, "pci") && 270 !STREQ(parent_type, "pciex")) { 271 ddi_prop_free(parent_type); 272 return; 273 } 274 275 ddi_prop_free(parent_type); 276 parent_type = NULL; 277 278 if (pci_config_setup(pdevi, &ppci_conf) != DDI_SUCCESS) { 279 /* No registers on root node, done with check */ 280 return; 281 } 282 283 data16 = pci_config_get16(ppci_conf, PCI_BCNF_BCNTRL); 284 pci_config_teardown(&ppci_conf); 285 286 if (!(data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)) { 287 softc->flags &= ~GFXP_FLAG_CONSOLE; 288 return; 289 } 290 291 pdevi = ddi_get_parent(pdevi); 292 } 293 } 294 295 int 296 gfxp_vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd, 297 gfxp_vgatext_softc_ptr_t ptr) 298 { 299 struct vgatext_softc *softc = (struct vgatext_softc *)ptr; 300 int unit = ddi_get_instance(devi); 301 int error; 302 char *parent_type = NULL; 303 int reg_rnumber; 304 off_t reg_offset; 305 off_t mem_offset; 306 char *cons; 307 int pci_pcie_bus = 0; 308 int value; 309 310 switch (cmd) { 311 case DDI_ATTACH: 312 break; 313 314 case DDI_RESUME: 315 vgatext_resume(softc); 316 return (DDI_SUCCESS); 317 318 default: 319 return (DDI_FAILURE); 320 } 321 322 /* DDI_ATTACH */ 323 324 softc->devi = devi; /* Copy and init DEVI */ 325 326 softc->polledio.arg = (struct vis_polledio_arg *)softc; 327 softc->polledio.display = vgatext_polled_display; 328 softc->polledio.copy = vgatext_polled_copy; 329 softc->polledio.cursor = vgatext_polled_cursor; 330 331 mutex_init(&(softc->lock), NULL, MUTEX_DRIVER, NULL); 332 333 error = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(devi), 334 DDI_PROP_DONTPASS, "device_type", &parent_type); 335 if (error != DDI_SUCCESS) { 336 cmn_err(CE_WARN, MYNAME ": can't determine parent type."); 337 goto fail; 338 } 339 340 /* Not enable AGP and DRM by default */ 341 if (STREQ(parent_type, "isa") || STREQ(parent_type, "eisa")) { 342 reg_rnumber = vgatext_get_isa_reg_index(devi, 1, VGA_REG_ADDR, 343 ®_offset); 344 if (reg_rnumber < 0) { 345 cmn_err(CE_WARN, 346 MYNAME 347 ": can't find reg entry for registers"); 348 error = DDI_FAILURE; 349 goto fail; 350 } 351 softc->fb_regno = vgatext_get_isa_reg_index(devi, 0, 352 VGA_MEM_ADDR, &mem_offset); 353 if (softc->fb_regno < 0) { 354 cmn_err(CE_WARN, 355 MYNAME 356 ": can't find reg entry for memory"); 357 error = DDI_FAILURE; 358 goto fail; 359 } 360 } else if (STREQ(parent_type, "pci") || STREQ(parent_type, "pciex")) { 361 pci_pcie_bus = 1; 362 reg_rnumber = vgatext_get_pci_reg_index(devi, 363 PCI_REG_ADDR_M|PCI_REG_REL_M, 364 PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR, 365 ®_offset); 366 if (reg_rnumber < 0) { 367 cmn_err(CE_WARN, 368 MYNAME 369 ": can't find reg entry for registers"); 370 error = DDI_FAILURE; 371 goto fail; 372 } 373 softc->fb_regno = vgatext_get_pci_reg_index(devi, 374 PCI_REG_ADDR_M|PCI_REG_REL_M, 375 PCI_ADDR_MEM32|PCI_RELOCAT_B, VGA_MEM_ADDR, 376 &mem_offset); 377 if (softc->fb_regno < 0) { 378 cmn_err(CE_WARN, 379 MYNAME 380 ": can't find reg entry for memory"); 381 error = DDI_FAILURE; 382 goto fail; 383 } 384 } else { 385 cmn_err(CE_WARN, MYNAME ": unknown parent type \"%s\".", 386 parent_type); 387 error = DDI_FAILURE; 388 goto fail; 389 } 390 ddi_prop_free(parent_type); 391 parent_type = NULL; 392 393 error = ddi_regs_map_setup(devi, reg_rnumber, 394 (caddr_t *)&softc->regs.addr, reg_offset, VGA_REG_SIZE, 395 &dev_attr, &softc->regs.handle); 396 if (error != DDI_SUCCESS) 397 goto fail; 398 softc->regs.mapped = B_TRUE; 399 400 softc->fb_size = VGA_MEM_SIZE; 401 402 error = ddi_regs_map_setup(devi, softc->fb_regno, 403 (caddr_t *)&softc->fb.addr, 404 mem_offset, softc->fb_size, 405 &dev_attr, &softc->fb.handle); 406 if (error != DDI_SUCCESS) 407 goto fail; 408 softc->fb.mapped = B_TRUE; 409 410 if (ddi_get8(softc->regs.handle, 411 softc->regs.addr + VGA_MISC_R) & VGA_MISC_IOA_SEL) 412 softc->text_base = (caddr_t)softc->fb.addr + VGA_COLOR_BASE; 413 else 414 softc->text_base = (caddr_t)softc->fb.addr + VGA_MONO_BASE; 415 416 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 417 DDI_PROP_DONTPASS, "console", &cons) == DDI_SUCCESS) { 418 if (strcmp(cons, "graphics") == 0) { 419 happyface_boot = 1; 420 vgatext_silent = 1; 421 softc->current_base = softc->shadow; 422 } else { 423 softc->current_base = softc->text_base; 424 } 425 ddi_prop_free(cons); 426 } else { 427 softc->current_base = softc->text_base; 428 } 429 430 error = ddi_prop_create(makedevice(DDI_MAJOR_T_UNKNOWN, unit), 431 devi, DDI_PROP_CANSLEEP, DDI_KERNEL_IOCTL, NULL, 0); 432 if (error != DDI_SUCCESS) 433 goto fail; 434 435 gfxp_check_for_console(devi, softc, pci_pcie_bus); 436 437 value = GFXP_IS_CONSOLE(softc) ? 1 : 0; 438 if (ddi_prop_update_int(DDI_DEV_T_NONE, devi, 439 "primary-controller", value) != DDI_SUCCESS) { 440 cmn_err(CE_WARN, 441 "Can not %s primary-controller " 442 "property for driver", value ? "set" : "clear"); 443 } 444 445 /* only do this if not in graphics mode */ 446 if ((vgatext_silent == 0) && (GFXP_IS_CONSOLE(softc))) { 447 vgatext_init(softc); 448 vgatext_save_colormap(softc); 449 } 450 451 return (DDI_SUCCESS); 452 453 fail: 454 if (parent_type != NULL) 455 ddi_prop_free(parent_type); 456 (void) gfxp_vgatext_detach(devi, DDI_DETACH, (void *)softc); 457 return (error); 458 } 459 460 /*ARGSUSED*/ 461 int 462 gfxp_vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd, 463 gfxp_vgatext_softc_ptr_t ptr) 464 { 465 struct vgatext_softc *softc = (struct vgatext_softc *)ptr; 466 467 (void) ddi_prop_remove(DDI_DEV_T_ANY, devi, "primary-controller"); 468 469 switch (cmd) { 470 471 case DDI_SUSPEND: 472 return (vgatext_suspend(softc)); 473 /* break; */ 474 case DDI_DETACH: 475 if (softc->fb.mapped) 476 ddi_regs_map_free(&softc->fb.handle); 477 if (softc->regs.mapped) 478 ddi_regs_map_free(&softc->regs.handle); 479 mutex_destroy(&(softc->lock)); 480 return (DDI_SUCCESS); 481 482 default: 483 cmn_err(CE_WARN, "gfxp_vgatext_detach: unknown cmd 0x%x\n", 484 cmd); 485 return (DDI_FAILURE); 486 } 487 } 488 489 /*ARGSUSED*/ 490 int 491 gfxp_vgatext_open(dev_t *devp, int flag, int otyp, cred_t *cred, 492 gfxp_vgatext_softc_ptr_t ptr) 493 { 494 struct vgatext_softc *softc = (struct vgatext_softc *)ptr; 495 496 if (softc == NULL || otyp == OTYP_BLK) 497 return (ENXIO); 498 499 return (0); 500 } 501 502 /*ARGSUSED*/ 503 int 504 gfxp_vgatext_close(dev_t devp, int flag, int otyp, cred_t *cred, 505 gfxp_vgatext_softc_ptr_t ptr) 506 { 507 return (0); 508 } 509 510 static int 511 do_gfx_ioctl(int cmd, intptr_t data, int mode, struct vgatext_softc *softc) 512 { 513 static char kernel_only[] = 514 "gfxp_vgatext_ioctl: %s is a kernel only ioctl"; 515 int err; 516 int kd_mode; 517 518 switch (cmd) { 519 case KDSETMODE: 520 return (vgatext_kdsetmode(softc, (int)data)); 521 522 case KDGETMODE: 523 kd_mode = softc->mode; 524 if (ddi_copyout(&kd_mode, (void *)data, sizeof (int), mode)) 525 return (EFAULT); 526 break; 527 528 case VIS_DEVINIT: 529 530 if (!(mode & FKIOCTL)) { 531 cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT"); 532 return (ENXIO); 533 } 534 535 err = vgatext_devinit(softc, (struct vis_devinit *)data); 536 if (err != 0) { 537 cmn_err(CE_WARN, 538 "gfxp_vgatext_ioctl: could not" 539 " initialize console"); 540 return (err); 541 } 542 break; 543 544 case VIS_CONSCOPY: /* move */ 545 { 546 struct vis_conscopy pma; 547 548 if (ddi_copyin((void *)data, &pma, 549 sizeof (struct vis_conscopy), mode)) 550 return (EFAULT); 551 552 vgatext_cons_copy(softc, &pma); 553 break; 554 } 555 556 case VIS_CONSDISPLAY: /* display */ 557 { 558 struct vis_consdisplay display_request; 559 560 if (ddi_copyin((void *)data, &display_request, 561 sizeof (display_request), mode)) 562 return (EFAULT); 563 564 vgatext_cons_display(softc, &display_request); 565 break; 566 } 567 568 case VIS_CONSCURSOR: 569 { 570 struct vis_conscursor cursor_request; 571 572 if (ddi_copyin((void *)data, &cursor_request, 573 sizeof (cursor_request), mode)) 574 return (EFAULT); 575 576 vgatext_cons_cursor(softc, &cursor_request); 577 578 if (cursor_request.action == VIS_GET_CURSOR && 579 ddi_copyout(&cursor_request, (void *)data, 580 sizeof (cursor_request), mode)) 581 return (EFAULT); 582 break; 583 } 584 585 case VIS_GETCMAP: 586 case VIS_PUTCMAP: 587 case FBIOPUTCMAP: 588 case FBIOGETCMAP: 589 /* 590 * At the moment, text mode is not considered to have 591 * a color map. 592 */ 593 return (EINVAL); 594 595 case FBIOGATTR: 596 if (copyout(&vgatext_attr, (void *)data, 597 sizeof (struct fbgattr))) 598 return (EFAULT); 599 break; 600 601 case FBIOGTYPE: 602 if (copyout(&vgatext_attr.fbtype, (void *)data, 603 sizeof (struct fbtype))) 604 return (EFAULT); 605 break; 606 607 default: 608 return (ENXIO); 609 } 610 return (0); 611 } 612 613 /*ARGSUSED*/ 614 int 615 gfxp_vgatext_ioctl( 616 dev_t dev, 617 int cmd, 618 intptr_t data, 619 int mode, 620 cred_t *cred, 621 int *rval, 622 gfxp_vgatext_softc_ptr_t ptr) 623 { 624 int err; 625 626 struct vgatext_softc *softc = (struct vgatext_softc *)ptr; 627 628 mutex_enter(&(softc->lock)); 629 err = do_gfx_ioctl(cmd, data, mode, softc); 630 mutex_exit(&(softc->lock)); 631 632 return (err); 633 } 634 635 /* 636 * vgatext_save_text 637 * vgatext_restore_textmode 638 * vgatext_suspend 639 * vgatext_resume 640 * 641 * Routines to save and restore contents of the VGA text area 642 * Mostly, this is to support Suspend/Resume operation for graphics 643 * device drivers. Here in the VGAtext common code, we simply squirrel 644 * away the contents of the hardware's text area during Suspend and then 645 * put it back during Resume 646 */ 647 static void 648 vgatext_save_text(struct vgatext_softc *softc) 649 { 650 unsigned i; 651 652 for (i = 0; i < sizeof (softc->shadow); i++) 653 softc->shadow[i] = softc->current_base[i]; 654 } 655 656 static void 657 vgatext_restore_textmode(struct vgatext_softc *softc) 658 { 659 unsigned i; 660 661 vgatext_init(softc); 662 for (i = 0; i < sizeof (softc->shadow); i++) { 663 softc->text_base[i] = softc->shadow[i]; 664 } 665 if (softc->cursor.visible) { 666 vgatext_set_cursor(softc, 667 softc->cursor.row, softc->cursor.col); 668 } 669 vgatext_restore_colormap(softc); 670 } 671 672 static int 673 vgatext_suspend(struct vgatext_softc *softc) 674 { 675 switch (softc->mode) { 676 case KD_TEXT: 677 vgatext_save_text(softc); 678 break; 679 680 case KD_GRAPHICS: 681 break; 682 683 default: 684 cmn_err(CE_WARN, MYNAME ": unknown mode in vgatext_suspend."); 685 return (DDI_FAILURE); 686 } 687 return (DDI_SUCCESS); 688 } 689 690 static void 691 vgatext_resume(struct vgatext_softc *softc) 692 { 693 694 switch (softc->mode) { 695 case KD_TEXT: 696 vgatext_restore_textmode(softc); 697 break; 698 699 case KD_GRAPHICS: 700 701 /* 702 * Upon RESUME, the graphics device will always actually 703 * be in TEXT mode even though the Xorg server did not 704 * make that mode change itself (the suspend code did). 705 * We want first, therefore, to restore textmode 706 * operation fully, and then the Xorg server will 707 * do the rest to restore the device to its 708 * (hi resolution) graphics mode 709 */ 710 vgatext_restore_textmode(softc); 711 #if defined(USE_BORDERS) 712 vgatext_init_graphics(softc); 713 #endif 714 break; 715 default: 716 cmn_err(CE_WARN, MYNAME ": unknown mode in vgatext_resume."); 717 break; 718 } 719 } 720 721 static void 722 vgatext_progressbar_stop() 723 { 724 extern void progressbar_stop(void); 725 726 if (vgatext_silent == 1) { 727 vgatext_silent = 0; 728 progressbar_stop(); 729 } 730 } 731 732 static void 733 vgatext_kdsettext(struct vgatext_softc *softc) 734 { 735 int i; 736 737 vgatext_init(softc); 738 for (i = 0; i < sizeof (softc->shadow); i++) { 739 softc->text_base[i] = softc->shadow[i]; 740 } 741 softc->current_base = softc->text_base; 742 if (softc->cursor.visible) { 743 vgatext_set_cursor(softc, 744 softc->cursor.row, softc->cursor.col); 745 } 746 vgatext_restore_colormap(softc); 747 } 748 749 static void 750 vgatext_kdsetgraphics(struct vgatext_softc *softc) 751 { 752 vgatext_progressbar_stop(); 753 vgatext_save_text(softc); 754 softc->current_base = softc->shadow; 755 #if defined(USE_BORDERS) 756 vgatext_init_graphics(softc); 757 #endif 758 } 759 760 static int 761 vgatext_kdsetmode(struct vgatext_softc *softc, int mode) 762 { 763 if ((mode == softc->mode) || (!GFXP_IS_CONSOLE(softc))) 764 return (0); 765 766 switch (mode) { 767 case KD_TEXT: 768 vgatext_kdsettext(softc); 769 break; 770 771 case KD_GRAPHICS: 772 vgatext_kdsetgraphics(softc); 773 break; 774 775 case KD_RESETTEXT: 776 /* 777 * In order to avoid racing with a starting X server, 778 * this needs to be a test and set that is performed in 779 * a single (softc->lock protected) ioctl into this driver. 780 */ 781 if (softc->mode == KD_TEXT && vgatext_silent == 1) { 782 vgatext_progressbar_stop(); 783 vgatext_kdsettext(softc); 784 } 785 break; 786 787 default: 788 return (EINVAL); 789 } 790 791 softc->mode = mode; 792 return (0); 793 } 794 795 /*ARGSUSED*/ 796 int 797 gfxp_vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len, 798 size_t *maplen, uint_t model, void *ptr) 799 { 800 struct vgatext_softc *softc = (struct vgatext_softc *)ptr; 801 int err; 802 size_t length; 803 804 805 if (softc == NULL) { 806 cmn_err(CE_WARN, "vgatext: Can't find softstate"); 807 return (-1); 808 } 809 810 if (!(off >= VGA_MMAP_FB_BASE && 811 off < VGA_MMAP_FB_BASE + softc->fb_size)) { 812 cmn_err(CE_WARN, "vgatext: Can't map offset 0x%llx", off); 813 return (-1); 814 } 815 816 if (off + len > VGA_MMAP_FB_BASE + softc->fb_size) 817 length = VGA_MMAP_FB_BASE + softc->fb_size - off; 818 else 819 length = len; 820 821 if ((err = devmap_devmem_setup(dhp, softc->devi, 822 NULL, softc->fb_regno, off - VGA_MMAP_FB_BASE, 823 length, PROT_ALL, 0, &dev_attr)) < 0) { 824 return (err); 825 } 826 827 828 *maplen = length; 829 return (0); 830 } 831 832 833 static int 834 vgatext_devinit(struct vgatext_softc *softc, struct vis_devinit *data) 835 { 836 /* initialize console instance */ 837 data->version = VIS_CONS_REV; 838 data->width = TEXT_COLS; 839 data->height = TEXT_ROWS; 840 data->linebytes = TEXT_COLS; 841 data->depth = 4; 842 data->mode = VIS_TEXT; 843 data->polledio = &softc->polledio; 844 845 return (0); 846 } 847 848 /* 849 * display a string on the screen at (row, col) 850 * assume it has been cropped to fit. 851 */ 852 853 static void 854 vgatext_cons_display(struct vgatext_softc *softc, struct vis_consdisplay *da) 855 { 856 unsigned char *string; 857 int i; 858 unsigned char attr; 859 struct cgatext { 860 unsigned char ch; 861 unsigned char attr; 862 }; 863 struct cgatext *addr; 864 865 /* 866 * Sanity checks. This is a last-ditch effort to avoid damage 867 * from brokenness or maliciousness above. 868 */ 869 if (da->row < 0 || da->row >= TEXT_ROWS || 870 da->col < 0 || da->col >= TEXT_COLS || 871 da->col + da->width > TEXT_COLS) 872 return; 873 874 /* 875 * To be fully general, we should copyin the data. This is not 876 * really relevant for this text-only driver, but a graphical driver 877 * should support these ioctls from userland to enable simple 878 * system startup graphics. 879 */ 880 attr = (solaris_color_to_pc_color[da->bg_color & 0xf] << 4) 881 | solaris_color_to_pc_color[da->fg_color & 0xf]; 882 string = da->data; 883 addr = (struct cgatext *)softc->current_base 884 + (da->row * TEXT_COLS + da->col); 885 for (i = 0; i < da->width; i++) { 886 addr->ch = string[i]; 887 addr->attr = attr; 888 addr++; 889 } 890 } 891 892 static void 893 vgatext_polled_display( 894 struct vis_polledio_arg *arg, 895 struct vis_consdisplay *da) 896 { 897 vgatext_cons_display((struct vgatext_softc *)arg, da); 898 } 899 900 /* 901 * screen-to-screen copy 902 */ 903 904 static void 905 vgatext_cons_copy(struct vgatext_softc *softc, struct vis_conscopy *ma) 906 { 907 unsigned short *from; 908 unsigned short *to; 909 int cnt; 910 screen_size_t chars_per_row; 911 unsigned short *to_row_start; 912 unsigned short *from_row_start; 913 screen_size_t rows_to_move; 914 unsigned short *base; 915 916 /* 917 * Sanity checks. Note that this is a last-ditch effort to avoid 918 * damage caused by broken-ness or maliciousness above. 919 */ 920 if (ma->s_col < 0 || ma->s_col >= TEXT_COLS || 921 ma->s_row < 0 || ma->s_row >= TEXT_ROWS || 922 ma->e_col < 0 || ma->e_col >= TEXT_COLS || 923 ma->e_row < 0 || ma->e_row >= TEXT_ROWS || 924 ma->t_col < 0 || ma->t_col >= TEXT_COLS || 925 ma->t_row < 0 || ma->t_row >= TEXT_ROWS || 926 ma->s_col > ma->e_col || 927 ma->s_row > ma->e_row) 928 return; 929 930 /* 931 * Remember we're going to copy shorts because each 932 * character/attribute pair is 16 bits. 933 */ 934 chars_per_row = ma->e_col - ma->s_col + 1; 935 rows_to_move = ma->e_row - ma->s_row + 1; 936 937 /* More sanity checks. */ 938 if (ma->t_row + rows_to_move > TEXT_ROWS || 939 ma->t_col + chars_per_row > TEXT_COLS) 940 return; 941 942 base = (unsigned short *)softc->current_base; 943 944 to_row_start = base + ((ma->t_row * TEXT_COLS) + ma->t_col); 945 from_row_start = base + ((ma->s_row * TEXT_COLS) + ma->s_col); 946 947 if (to_row_start < from_row_start) { 948 while (rows_to_move-- > 0) { 949 to = to_row_start; 950 from = from_row_start; 951 to_row_start += TEXT_COLS; 952 from_row_start += TEXT_COLS; 953 for (cnt = chars_per_row; cnt-- > 0; ) 954 *to++ = *from++; 955 } 956 } else { 957 /* 958 * Offset to the end of the region and copy backwards. 959 */ 960 cnt = rows_to_move * TEXT_COLS + chars_per_row; 961 to_row_start += cnt; 962 from_row_start += cnt; 963 964 while (rows_to_move-- > 0) { 965 to_row_start -= TEXT_COLS; 966 from_row_start -= TEXT_COLS; 967 to = to_row_start; 968 from = from_row_start; 969 for (cnt = chars_per_row; cnt-- > 0; ) 970 *--to = *--from; 971 } 972 } 973 } 974 975 static void 976 vgatext_polled_copy( 977 struct vis_polledio_arg *arg, 978 struct vis_conscopy *ca) 979 { 980 vgatext_cons_copy((struct vgatext_softc *)arg, ca); 981 } 982 983 984 static void 985 vgatext_cons_cursor(struct vgatext_softc *softc, struct vis_conscursor *ca) 986 { 987 if (vgatext_silent) 988 return; 989 990 switch (ca->action) { 991 case VIS_HIDE_CURSOR: 992 softc->cursor.visible = B_FALSE; 993 if (softc->current_base == softc->text_base) 994 vgatext_hide_cursor(softc); 995 break; 996 case VIS_DISPLAY_CURSOR: 997 /* 998 * Sanity check. This is a last-ditch effort to avoid 999 * damage from brokenness or maliciousness above. 1000 */ 1001 if (ca->col < 0 || ca->col >= TEXT_COLS || 1002 ca->row < 0 || ca->row >= TEXT_ROWS) 1003 return; 1004 1005 softc->cursor.visible = B_TRUE; 1006 softc->cursor.col = ca->col; 1007 softc->cursor.row = ca->row; 1008 if (softc->current_base == softc->text_base) 1009 vgatext_set_cursor(softc, ca->row, ca->col); 1010 break; 1011 case VIS_GET_CURSOR: 1012 if (softc->current_base == softc->text_base) { 1013 vgatext_get_cursor(softc, &ca->row, &ca->col); 1014 } 1015 break; 1016 } 1017 } 1018 1019 static void 1020 vgatext_polled_cursor( 1021 struct vis_polledio_arg *arg, 1022 struct vis_conscursor *ca) 1023 { 1024 vgatext_cons_cursor((struct vgatext_softc *)arg, ca); 1025 } 1026 1027 1028 1029 /*ARGSUSED*/ 1030 static void 1031 vgatext_hide_cursor(struct vgatext_softc *softc) 1032 { 1033 /* Nothing at present */ 1034 } 1035 1036 static void 1037 vgatext_set_cursor(struct vgatext_softc *softc, int row, int col) 1038 { 1039 short addr; 1040 1041 if (vgatext_silent) 1042 return; 1043 1044 addr = row * TEXT_COLS + col; 1045 1046 vga_set_crtc(&softc->regs, VGA_CRTC_CLAH, addr >> 8); 1047 vga_set_crtc(&softc->regs, VGA_CRTC_CLAL, addr & 0xff); 1048 } 1049 1050 static int vga_row, vga_col; 1051 1052 static void 1053 vgatext_get_cursor(struct vgatext_softc *softc, 1054 screen_pos_t *row, screen_pos_t *col) 1055 { 1056 short addr; 1057 1058 addr = (vga_get_crtc(&softc->regs, VGA_CRTC_CLAH) << 8) + 1059 vga_get_crtc(&softc->regs, VGA_CRTC_CLAL); 1060 1061 vga_row = *row = addr / TEXT_COLS; 1062 vga_col = *col = addr % TEXT_COLS; 1063 } 1064 1065 /* 1066 * This code is experimental. It's only enabled if console is 1067 * set to graphics, a preliminary implementation of happyface boot. 1068 */ 1069 static void 1070 vgatext_set_text(struct vgatext_softc *softc) 1071 { 1072 int i; 1073 1074 if (happyface_boot == 0) 1075 return; 1076 1077 /* we are in graphics mode, set to text 80X25 mode */ 1078 1079 /* set misc registers */ 1080 vga_set_reg(&softc->regs, VGA_MISC_W, VGA_MISC_TEXT); 1081 1082 /* set sequencer registers */ 1083 vga_set_seq(&softc->regs, VGA_SEQ_RST_SYN, 1084 (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) & 1085 ~VGA_SEQ_RST_SYN_NO_SYNC_RESET)); 1086 for (i = 1; i < NUM_SEQ_REG; i++) { 1087 vga_set_seq(&softc->regs, i, VGA_SEQ_TEXT[i]); 1088 } 1089 vga_set_seq(&softc->regs, VGA_SEQ_RST_SYN, 1090 (vga_get_seq(&softc->regs, VGA_SEQ_RST_SYN) | 1091 VGA_SEQ_RST_SYN_NO_ASYNC_RESET | 1092 VGA_SEQ_RST_SYN_NO_SYNC_RESET)); 1093 1094 /* set crt controller registers */ 1095 vga_set_crtc(&softc->regs, VGA_CRTC_VRE, 1096 (vga_get_crtc(&softc->regs, VGA_CRTC_VRE) & 1097 ~VGA_CRTC_VRE_LOCK)); 1098 for (i = 0; i < NUM_CRTC_REG; i++) { 1099 vga_set_crtc(&softc->regs, i, VGA_CRTC_TEXT[i]); 1100 } 1101 1102 /* set graphics controller registers */ 1103 for (i = 0; i < NUM_GRC_REG; i++) { 1104 vga_set_grc(&softc->regs, i, VGA_GRC_TEXT[i]); 1105 } 1106 1107 /* set attribute registers */ 1108 for (i = 0; i < NUM_ATR_REG; i++) { 1109 vga_set_atr(&softc->regs, i, VGA_ATR_TEXT[i]); 1110 } 1111 1112 /* set palette */ 1113 for (i = 0; i < VGA_TEXT_CMAP_ENTRIES; i++) { 1114 vga_put_cmap(&softc->regs, i, VGA_TEXT_PALETTES[i][0] << 2, 1115 VGA_TEXT_PALETTES[i][1] << 2, 1116 VGA_TEXT_PALETTES[i][2] << 2); 1117 } 1118 for (i = VGA_TEXT_CMAP_ENTRIES; i < VGA8_CMAP_ENTRIES; i++) { 1119 vga_put_cmap(&softc->regs, i, 0, 0, 0); 1120 } 1121 1122 vgatext_save_colormap(softc); 1123 } 1124 1125 static void 1126 vgatext_init(struct vgatext_softc *softc) 1127 { 1128 unsigned char atr_mode; 1129 1130 atr_mode = vga_get_atr(&softc->regs, VGA_ATR_MODE); 1131 if (atr_mode & VGA_ATR_MODE_GRAPH) 1132 vgatext_set_text(softc); 1133 atr_mode = vga_get_atr(&softc->regs, VGA_ATR_MODE); 1134 atr_mode &= ~VGA_ATR_MODE_BLINK; 1135 atr_mode &= ~VGA_ATR_MODE_9WIDE; 1136 vga_set_atr(&softc->regs, VGA_ATR_MODE, atr_mode); 1137 #if defined(USE_BORDERS) 1138 vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR, 1139 vga_get_atr(&softc->regs, VGA_BRIGHT_WHITE)); 1140 #else 1141 vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR, 1142 vga_get_atr(&softc->regs, VGA_BLACK)); 1143 #endif 1144 vgatext_setfont(softc); /* need selectable font? */ 1145 } 1146 1147 #if defined(USE_BORDERS) 1148 static void 1149 vgatext_init_graphics(struct vgatext_softc *softc) 1150 { 1151 vga_set_atr(&softc->regs, VGA_ATR_BDR_CLR, 1152 vga_get_atr(&softc->regs, VGA_BLACK)); 1153 } 1154 #endif 1155 1156 static void 1157 vgatext_setfont(struct vgatext_softc *softc) 1158 { 1159 extern unsigned char *ENCODINGS[]; 1160 unsigned char *from; 1161 unsigned char *to; 1162 int i; 1163 int j; 1164 int bpc; 1165 1166 /* 1167 * The newboot code to use font plane 2 breaks NVIDIA 1168 * (and some ATI) behavior. Revert back to the S10 1169 * code. 1170 */ 1171 1172 /* 1173 * I'm embarassed to say that I don't know what these magic 1174 * sequences do, other than at the high level of "set the 1175 * memory window to allow font setup". I stole them straight 1176 * from "kd"... 1177 */ 1178 vga_set_seq(&softc->regs, 0x02, 0x04); 1179 vga_set_seq(&softc->regs, 0x04, 0x06); 1180 vga_set_grc(&softc->regs, 0x05, 0x00); 1181 vga_set_grc(&softc->regs, 0x06, 0x04); 1182 1183 /* 1184 * This assumes 8x16 characters, which yield the traditional 80x25 1185 * screen. It really should support other character heights. 1186 */ 1187 bpc = 16; 1188 for (i = 0; i < 256; i++) { 1189 from = ENCODINGS[i]; 1190 to = (unsigned char *)softc->fb.addr + i * 0x20; 1191 for (j = 0; j < bpc; j++) 1192 *to++ = *from++; 1193 } 1194 1195 vga_set_seq(&softc->regs, 0x02, 0x03); 1196 vga_set_seq(&softc->regs, 0x04, 0x02); 1197 vga_set_grc(&softc->regs, 0x04, 0x00); 1198 vga_set_grc(&softc->regs, 0x05, 0x10); 1199 vga_set_grc(&softc->regs, 0x06, 0x0e); 1200 1201 } 1202 1203 static void 1204 vgatext_save_colormap(struct vgatext_softc *softc) 1205 { 1206 int i; 1207 1208 for (i = 0; i < VGA_ATR_NUM_PLT; i++) { 1209 softc->attrib_palette[i] = vga_get_atr(&softc->regs, i); 1210 } 1211 for (i = 0; i < VGA8_CMAP_ENTRIES; i++) { 1212 vga_get_cmap(&softc->regs, i, 1213 &softc->colormap[i].red, 1214 &softc->colormap[i].green, 1215 &softc->colormap[i].blue); 1216 } 1217 } 1218 1219 static void 1220 vgatext_restore_colormap(struct vgatext_softc *softc) 1221 { 1222 int i; 1223 1224 for (i = 0; i < VGA_ATR_NUM_PLT; i++) { 1225 vga_set_atr(&softc->regs, i, softc->attrib_palette[i]); 1226 } 1227 for (i = 0; i < VGA8_CMAP_ENTRIES; i++) { 1228 vga_put_cmap(&softc->regs, i, 1229 softc->colormap[i].red, 1230 softc->colormap[i].green, 1231 softc->colormap[i].blue); 1232 } 1233 } 1234 1235 /* 1236 * search the entries of the "reg" property for one which has the desired 1237 * combination of phys_hi bits and contains the desired address. 1238 * 1239 * This version searches a PCI-style "reg" property. It was prompted by 1240 * issues surrounding the presence or absence of an entry for the ROM: 1241 * (a) a transition problem with PowerPC Virtual Open Firmware 1242 * (b) uncertainty as to whether an entry will be included on a device 1243 * with ROM support (and so an "active" ROM base address register), 1244 * but no ROM actually installed. 1245 * 1246 * See the note below on vgatext_get_isa_reg_index for the reasons for 1247 * returning the offset. 1248 * 1249 * Note that this routine may not be fully general; it is intended for the 1250 * specific purpose of finding a couple of particular VGA reg entries and 1251 * may not be suitable for all reg-searching purposes. 1252 */ 1253 static int 1254 vgatext_get_pci_reg_index( 1255 dev_info_t *const devi, 1256 unsigned long himask, 1257 unsigned long hival, 1258 unsigned long addr, 1259 off_t *offset) 1260 { 1261 1262 int length, index; 1263 pci_regspec_t *reg; 1264 1265 if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 1266 "reg", (caddr_t)®, &length) != DDI_PROP_SUCCESS) { 1267 return (-1); 1268 } 1269 1270 for (index = 0; index < length / sizeof (pci_regspec_t); index++) { 1271 if ((reg[index].pci_phys_hi & himask) != hival) 1272 continue; 1273 if (reg[index].pci_size_hi != 0) 1274 continue; 1275 if (reg[index].pci_phys_mid != 0) 1276 continue; 1277 if (reg[index].pci_phys_low > addr) 1278 continue; 1279 if (reg[index].pci_phys_low + reg[index].pci_size_low <= addr) 1280 continue; 1281 1282 *offset = addr - reg[index].pci_phys_low; 1283 kmem_free(reg, (size_t)length); 1284 return (index); 1285 } 1286 kmem_free(reg, (size_t)length); 1287 1288 return (-1); 1289 } 1290 1291 /* 1292 * search the entries of the "reg" property for one which has the desired 1293 * combination of phys_hi bits and contains the desired address. 1294 * 1295 * This version searches a ISA-style "reg" property. It was prompted by 1296 * issues surrounding 8514/A support. By IEEE 1275 compatibility conventions, 1297 * 8514/A registers should have been added after all standard VGA registers. 1298 * Unfortunately, the Solaris/Intel device configuration framework 1299 * (a) lists the 8514/A registers before the video memory, and then 1300 * (b) also sorts the entries so that I/O entries come before memory 1301 * entries. 1302 * 1303 * It returns the "reg" index and offset into that register set. 1304 * The offset is needed because there exist (broken?) BIOSes that 1305 * report larger ranges enclosing the standard ranges. One reports 1306 * 0x3bf for 0x21 instead of 0x3c0 for 0x20, for instance. Using the 1307 * offset adjusts for this difference in the base of the register set. 1308 * 1309 * Note that this routine may not be fully general; it is intended for the 1310 * specific purpose of finding a couple of particular VGA reg entries and 1311 * may not be suitable for all reg-searching purposes. 1312 */ 1313 static int 1314 vgatext_get_isa_reg_index( 1315 dev_info_t *const devi, 1316 unsigned long hival, 1317 unsigned long addr, 1318 off_t *offset) 1319 { 1320 1321 int length, index; 1322 struct regspec *reg; 1323 1324 if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 1325 "reg", (caddr_t)®, &length) != DDI_PROP_SUCCESS) { 1326 return (-1); 1327 } 1328 1329 for (index = 0; index < length / sizeof (struct regspec); index++) { 1330 if (reg[index].regspec_bustype != hival) 1331 continue; 1332 if (reg[index].regspec_addr > addr) 1333 continue; 1334 if (reg[index].regspec_addr + reg[index].regspec_size <= addr) 1335 continue; 1336 1337 *offset = addr - reg[index].regspec_addr; 1338 kmem_free(reg, (size_t)length); 1339 return (index); 1340 } 1341 kmem_free(reg, (size_t)length); 1342 1343 return (-1); 1344 } 1345 1346 /* 1347 * This vgatext function is used to return the fb, and reg pointers 1348 * and handles for peer graphics drivers. 1349 */ 1350 1351 void 1352 vgatext_return_pointers(struct vgatext_softc *softc, 1353 struct vgaregmap *fbs, 1354 struct vgaregmap *regss) 1355 { 1356 1357 fbs->addr = softc->fb.addr; 1358 fbs->handle = softc->fb.handle; 1359 fbs->mapped = softc->fb.mapped; 1360 regss->addr = softc->regs.addr; 1361 regss->handle = softc->regs.handle; 1362 regss->mapped = softc->regs.mapped; 1363 } 1364