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