1 /* $NetBSD: ofb.c,v 1.43 2005/12/11 12:18:03 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1995, 1996 Carnegie-Mellon University. 5 * All rights reserved. 6 * 7 * Author: Chris G. Demetriou 8 * 9 * Permission to use, copy, modify and distribute this software and 10 * its documentation is hereby granted, provided that both the copyright 11 * notice and this permission notice appear in all copies of the 12 * software, derivative works or modified versions, and any portions 13 * thereof, and that both notices appear in supporting documentation. 14 * 15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 17 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 18 * 19 * Carnegie Mellon requests users of this software to return to 20 * 21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 22 * School of Computer Science 23 * Carnegie Mellon University 24 * Pittsburgh PA 15213-3890 25 * 26 * any improvements or extensions that they make and grant Carnegie the 27 * rights to redistribute these changes. 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: ofb.c,v 1.43 2005/12/11 12:18:03 christos Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/buf.h> 35 #include <sys/conf.h> 36 #include <sys/device.h> 37 #include <sys/ioctl.h> 38 #include <sys/kernel.h> 39 #include <sys/malloc.h> 40 #include <sys/systm.h> 41 42 #include <uvm/uvm_extern.h> 43 44 #include <dev/pci/pcidevs.h> 45 #include <dev/pci/pcireg.h> 46 #include <dev/pci/pcivar.h> 47 #include <dev/pci/pciio.h> 48 49 #include <dev/wscons/wsconsio.h> 50 #include <dev/wscons/wsdisplayvar.h> 51 #include <dev/rasops/rasops.h> 52 53 #include <dev/ofw/openfirm.h> 54 #include <dev/ofw/ofw_pci.h> 55 56 #include <machine/bus.h> 57 #include <machine/autoconf.h> 58 #include <machine/grfioctl.h> 59 60 #include <powerpc/oea/bat.h> 61 62 #include <macppc/dev/ofbvar.h> 63 64 #if OFB_ENABLE_CACHE 65 int ofb_enable_cache = 1; 66 #else 67 int ofb_enable_cache = 0; 68 #endif 69 70 int ofbmatch __P((struct device *, struct cfdata *, void *)); 71 void ofbattach __P((struct device *, struct device *, void *)); 72 int ofbprint __P((void *, const char *)); 73 74 CFATTACH_DECL(ofb, sizeof(struct ofb_softc), 75 ofbmatch, ofbattach, NULL, NULL); 76 77 struct ofb_devconfig ofb_console_dc; 78 79 struct wsscreen_descr ofb_stdscreen = { 80 "std", 81 0, 0, /* will be filled in -- XXX shouldn't, it's global */ 82 0, 83 0, 0, 84 WSSCREEN_REVERSE 85 }; 86 87 const struct wsscreen_descr *_ofb_scrlist[] = { 88 &ofb_stdscreen, 89 /* XXX other formats, graphics screen? */ 90 }; 91 92 struct wsscreen_list ofb_screenlist = { 93 sizeof(_ofb_scrlist) / sizeof(struct wsscreen_descr *), _ofb_scrlist 94 }; 95 96 static int ofb_ioctl __P((void *, u_long, caddr_t, int, struct lwp *)); 97 static paddr_t ofb_mmap __P((void *, off_t, int)); 98 static int ofb_alloc_screen __P((void *, const struct wsscreen_descr *, 99 void **, int *, int *, long *)); 100 static void ofb_free_screen __P((void *, void *)); 101 static int ofb_show_screen __P((void *, void *, int, 102 void (*) (void *, int, int), void *)); 103 static int copy_rom_font __P((void)); 104 105 struct wsdisplay_accessops ofb_accessops = { 106 ofb_ioctl, 107 ofb_mmap, 108 ofb_alloc_screen, 109 ofb_free_screen, 110 ofb_show_screen, 111 0 /* load_font */ 112 }; 113 114 static struct wsdisplay_font openfirm6x11; 115 116 static void ofb_common_init __P((int, struct ofb_devconfig *)); 117 static int ofb_getcmap __P((struct ofb_softc *, struct wsdisplay_cmap *)); 118 static int ofb_putcmap __P((struct ofb_softc *, struct wsdisplay_cmap *)); 119 120 int 121 ofbmatch(parent, match, aux) 122 struct device *parent; 123 struct cfdata *match; 124 void *aux; 125 { 126 struct pci_attach_args *pa = aux; 127 128 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE && 129 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_CONTROL) 130 return 1; 131 132 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY) 133 return 1; 134 135 return 0; 136 } 137 138 void 139 ofbattach(parent, self, aux) 140 struct device *parent, *self; 141 void *aux; 142 { 143 struct ofb_softc *sc = (struct ofb_softc *)self; 144 struct pci_attach_args *pa = aux; 145 struct wsemuldisplaydev_attach_args a; 146 int console, node; 147 struct ofb_devconfig *dc; 148 char devinfo[256]; 149 150 sc->sc_memt = pa->pa_memt; 151 sc->sc_iot = pa->pa_iot; 152 153 console = ofb_is_console(); 154 155 if (console) { 156 dc = &ofb_console_dc; 157 node = dc->dc_node; 158 sc->nscreens = 1; 159 } else { 160 int len; 161 162 dc = malloc(sizeof(struct ofb_devconfig), M_DEVBUF, M_WAITOK); 163 memset(dc, 0, sizeof(struct ofb_devconfig)); 164 node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag); 165 if (node == 0) { 166 printf(": ofdev not found\n"); 167 return; 168 } 169 170 /* XXX There are two child screens on PowerBook. */ 171 memset(devinfo, 0, sizeof(devinfo)); 172 OF_getprop(node, "device_type", devinfo, sizeof(devinfo)); 173 len = strlen(devinfo); 174 if (strcmp(devinfo + len - 7, "-parent") == 0) 175 node = OF_child(node); 176 177 ofb_common_init(node, dc); 178 } 179 sc->sc_dc = dc; 180 181 sc->sc_pc = pa->pa_pc; 182 sc->sc_pcitag = pa->pa_tag; 183 184 /* XXX */ 185 if (OF_getprop(node, "assigned-addresses", sc->sc_addrs, 186 sizeof(sc->sc_addrs)) == -1) { 187 node = OF_parent(node); 188 OF_getprop(node, "assigned-addresses", sc->sc_addrs, 189 sizeof(sc->sc_addrs)); 190 } 191 192 if (dc->dc_paddr == 0) { 193 printf(": cannot map framebuffer\n"); 194 return; 195 } 196 197 /* 198 * Clear the screen here, instead of above, in case 199 * ofb_common_init() failed to map the framebuffer. 200 */ 201 if (!console) { 202 int i, screenbytes; 203 204 screenbytes = dc->dc_ri.ri_stride * dc->dc_ri.ri_height; 205 for (i = 0; i < screenbytes; i += sizeof(u_int32_t)) 206 *(u_int32_t *)(dc->dc_paddr + i) = 0xffffffff; 207 } 208 209 pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo)); 210 printf(": %s\n", devinfo); 211 printf("%s: %d x %d, %dbpp\n", self->dv_xname, 212 dc->dc_ri.ri_width, dc->dc_ri.ri_height, dc->dc_ri.ri_depth); 213 214 sc->sc_cmap_red[0] = sc->sc_cmap_green[0] = sc->sc_cmap_blue[0] = 0; 215 sc->sc_cmap_red[15] = sc->sc_cmap_red[255] = 0xff; 216 sc->sc_cmap_green[15] = sc->sc_cmap_green[255] = 0xff; 217 sc->sc_cmap_blue[15] = sc->sc_cmap_blue[255] = 0xff; 218 219 a.console = console; 220 a.scrdata = &ofb_screenlist; 221 a.accessops = &ofb_accessops; 222 a.accesscookie = sc; 223 224 config_found(self, &a, wsemuldisplaydevprint); 225 } 226 227 void 228 ofb_common_init(node, dc) 229 int node; 230 struct ofb_devconfig *dc; 231 { 232 struct rasops_info *ri = &dc->dc_ri; 233 int32_t addr, width, height, linebytes, depth; 234 235 dc->dc_node = node; 236 if (dc->dc_ih == 0) { 237 char name[64]; 238 239 memset(name, 0, 64); 240 OF_package_to_path(node, name, sizeof(name)); 241 dc->dc_ih = OF_open(name); 242 } 243 244 /* XXX /chaos/control doesn't have "width", "height", ... */ 245 width = height = -1; 246 if (OF_getprop(node, "width", &width, 4) != 4) 247 OF_interpret("screen-width", 1, &width); 248 if (OF_getprop(node, "height", &height, 4) != 4) 249 OF_interpret("screen-height", 1, &height); 250 if (OF_getprop(node, "linebytes", &linebytes, 4) != 4) 251 linebytes = width; /* XXX */ 252 if (OF_getprop(node, "depth", &depth, 4) != 4) 253 depth = 8; /* XXX */ 254 if (OF_getprop(node, "address", &addr, 4) != 4) 255 OF_interpret("frame-buffer-adr", 1, &addr); 256 257 if (width == -1 || height == -1 || addr == 0 || addr == -1) 258 return; 259 260 dc->dc_paddr = addr; /* PA of the frame buffer */ 261 262 /* Make sure 0/0/0 is black and 255/255/255 is white. */ 263 OF_call_method_1("color!", dc->dc_ih, 4, 0, 0, 0, 0); 264 OF_call_method_1("color!", dc->dc_ih, 4, 255, 255, 255, 255); 265 266 /* Enable write-through cache. */ 267 if (ofb_enable_cache) { 268 vaddr_t va; 269 /* 270 * Let's try to find an empty BAT to use 271 */ 272 for (va = SEGMENT_LENGTH; va < (USER_SR << ADDR_SR_SHFT); 273 va += SEGMENT_LENGTH) { 274 if (battable[va >> ADDR_SR_SHFT].batu == 0) { 275 battable[va >> ADDR_SR_SHFT].batl = 276 BATL(addr & 0xf0000000, 277 BAT_G | BAT_W | BAT_M, BAT_PP_RW); 278 battable[va >> ADDR_SR_SHFT].batu = 279 BATL(va, BAT_BL_256M, BAT_Vs); 280 addr &= 0x0fffffff; 281 addr |= va; 282 break; 283 } 284 } 285 } 286 287 /* initialize rasops */ 288 ri->ri_width = width; 289 ri->ri_height = height; 290 ri->ri_depth = depth; 291 ri->ri_stride = linebytes; 292 ri->ri_bits = (char *)addr; 293 ri->ri_flg = RI_FORCEMONO | RI_FULLCLEAR | RI_CENTER; 294 295 /* If screen is smaller than 1024x768, use small font. */ 296 if ((width < 1024 || height < 768) && copy_rom_font() == 0) { 297 int cols, rows; 298 299 OF_interpret("#lines", 1, &rows); 300 OF_interpret("#columns", 1, &cols); 301 302 ri->ri_font = &openfirm6x11; 303 ri->ri_wsfcookie = -1; /* not using wsfont */ 304 rasops_init(ri, rows, cols); 305 306 ri->ri_xorigin = (width - cols * ri->ri_font->fontwidth) >> 1; 307 ri->ri_yorigin = (height - rows * ri->ri_font->fontheight) 308 >> 1; 309 ri->ri_bits = (char *)addr + ri->ri_xorigin + 310 ri->ri_stride * ri->ri_yorigin; 311 } else { 312 rasops_init(ri, 24, 80); 313 rasops_reconfig(ri, (height - 2) / ri->ri_font->fontheight, 314 ((width - 2) / ri->ri_font->fontwidth) & ~7); 315 } 316 317 /* black on white */ 318 ri->ri_devcmap[0] = 0xffffffff; /* bg */ 319 ri->ri_devcmap[1] = 0; /* fg */ 320 321 ofb_stdscreen.nrows = ri->ri_rows; 322 ofb_stdscreen.ncols = ri->ri_cols; 323 ofb_stdscreen.textops = &ri->ri_ops; 324 ofb_stdscreen.capabilities = ri->ri_caps; 325 } 326 327 int 328 ofb_is_console() 329 { 330 int chosen, stdout, node; 331 char type[16]; 332 333 chosen = OF_finddevice("/chosen"); 334 OF_getprop(chosen, "stdout", &stdout, 4); 335 node = OF_instance_to_package(stdout); 336 OF_getprop(node, "device_type", type, sizeof(type)); 337 if (strcmp(type, "display") == 0) 338 return 1; 339 else 340 return 0; 341 } 342 343 int 344 ofb_ioctl(v, cmd, data, flag, l) 345 void *v; 346 u_long cmd; 347 caddr_t data; 348 int flag; 349 struct lwp *l; 350 { 351 struct ofb_softc *sc = v; 352 struct ofb_devconfig *dc = sc->sc_dc; 353 struct wsdisplay_fbinfo *wdf; 354 struct grfinfo *gm; 355 356 switch (cmd) { 357 case WSDISPLAYIO_GTYPE: 358 *(u_int *)data = WSDISPLAY_TYPE_PCIMISC; /* XXX ? */ 359 return 0; 360 361 case WSDISPLAYIO_GINFO: 362 wdf = (void *)data; 363 wdf->height = dc->dc_ri.ri_height; 364 wdf->width = dc->dc_ri.ri_width; 365 wdf->depth = dc->dc_ri.ri_depth; 366 wdf->cmsize = 256; 367 return 0; 368 369 case WSDISPLAYIO_GETCMAP: 370 return ofb_getcmap(sc, (struct wsdisplay_cmap *)data); 371 372 case WSDISPLAYIO_PUTCMAP: 373 return ofb_putcmap(sc, (struct wsdisplay_cmap *)data); 374 375 /* XXX There are no way to know framebuffer pa from a user program. */ 376 case GRFIOCGINFO: 377 gm = (void *)data; 378 memset(gm, 0, sizeof(struct grfinfo)); 379 gm->gd_fbaddr = (caddr_t)dc->dc_paddr; 380 gm->gd_fbrowbytes = dc->dc_ri.ri_stride; 381 return 0; 382 /* PCI config read/write passthrough. */ 383 case PCI_IOC_CFGREAD: 384 case PCI_IOC_CFGWRITE: 385 return (pci_devioctl(sc->sc_pc, sc->sc_pcitag, 386 cmd, data, flag, l)); 387 } 388 return EPASSTHROUGH; 389 } 390 391 paddr_t 392 ofb_mmap(v, offset, prot) 393 void *v; 394 off_t offset; 395 int prot; 396 { 397 struct ofb_softc *sc = v; 398 struct ofb_devconfig *dc = sc->sc_dc; 399 struct rasops_info *ri = &dc->dc_ri; 400 u_int32_t *ap = sc->sc_addrs; 401 struct proc *me; 402 int i; 403 404 /* framebuffer at offset 0 */ 405 if (offset >=0 && offset < (ri->ri_stride * ri->ri_height)) 406 return dc->dc_paddr + offset; 407 408 /* 409 * restrict all other mappings to processes with superuser privileges 410 * or the kernel itself 411 */ 412 me = __curproc(); 413 if (me != NULL) { 414 if (suser(me->p_ucred, NULL) != 0) { 415 return -1; 416 } 417 } 418 419 /* let them mmap() 0xa0000 - 0xbffff if it's not covered above */ 420 #ifdef OFB_FAKE_VGA_FB 421 if (offset >=0xa0000 && offset < 0xbffff) 422 return dc->dc_paddr + offset - 0xa0000; 423 #endif 424 425 /* allow to map our IO space */ 426 if ((offset >= 0xf2000000) && (offset < 0xf2800000)) { 427 return bus_space_mmap(sc->sc_iot, offset-0xf2000000, 0, prot, 428 BUS_SPACE_MAP_LINEAR); 429 } 430 431 for (i = 0; i < 6; i++) { 432 switch (ap[0] & OFW_PCI_PHYS_HI_SPACEMASK) { 433 case OFW_PCI_PHYS_HI_SPACE_MEM32: 434 if (offset >= ap[2] && offset < ap[2] + ap[4]) 435 return bus_space_mmap(sc->sc_memt, offset, 0, 436 prot, BUS_SPACE_MAP_LINEAR); 437 } 438 ap += 5; 439 } 440 441 return -1; 442 } 443 444 int 445 ofb_alloc_screen(v, type, cookiep, curxp, curyp, attrp) 446 void *v; 447 const struct wsscreen_descr *type; 448 void **cookiep; 449 int *curxp, *curyp; 450 long *attrp; 451 { 452 struct ofb_softc *sc = v; 453 struct rasops_info *ri = &sc->sc_dc->dc_ri; 454 long defattr; 455 456 if (sc->nscreens > 0) 457 return (ENOMEM); 458 459 *cookiep = ri; /* one and only for now */ 460 *curxp = 0; 461 *curyp = 0; 462 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 463 *attrp = defattr; 464 sc->nscreens++; 465 return 0; 466 } 467 468 void 469 ofb_free_screen(v, cookie) 470 void *v; 471 void *cookie; 472 { 473 struct ofb_softc *sc = v; 474 475 if (sc->sc_dc == &ofb_console_dc) 476 panic("ofb_free_screen: console"); 477 478 sc->nscreens--; 479 } 480 481 int 482 ofb_show_screen(v, cookie, waitok, cb, cbarg) 483 void *v; 484 void *cookie; 485 int waitok; 486 void (*cb) __P((void *, int, int)); 487 void *cbarg; 488 { 489 490 return (0); 491 } 492 493 int 494 ofb_cnattach() 495 { 496 struct ofb_devconfig *dc = &ofb_console_dc; 497 struct rasops_info *ri = &dc->dc_ri; 498 long defattr; 499 int crow = 0; 500 int chosen, stdout, node; 501 502 chosen = OF_finddevice("/chosen"); 503 OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)); 504 node = OF_instance_to_package(stdout); 505 dc->dc_ih = stdout; 506 507 /* get current cursor position */ 508 OF_interpret("line#", 1, &crow); 509 510 /* move (rom monitor) cursor to the lowest line - 1 */ 511 OF_interpret("#lines 2 - to line#", 0); 512 513 ofb_common_init(node, dc); 514 515 if (ri->ri_width >= 1024 && ri->ri_height >= 768) { 516 int i, screenbytes = ri->ri_stride * ri->ri_height; 517 518 for (i = 0; i < screenbytes; i += sizeof(u_int32_t)) 519 *(u_int32_t *)(dc->dc_paddr + i) = 0xffffffff; 520 crow = 0; 521 } 522 523 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 524 wsdisplay_cnattach(&ofb_stdscreen, ri, 0, crow, defattr); 525 526 return 0; 527 } 528 529 int 530 copy_rom_font() 531 { 532 u_char *romfont; 533 int char_width, char_height; 534 int chosen, mmu, m, e; 535 536 /* Get ROM FONT address. */ 537 OF_interpret("font-adr", 1, &romfont); 538 if (romfont == NULL) 539 return -1; 540 541 chosen = OF_finddevice("/chosen"); 542 OF_getprop(chosen, "mmu", &mmu, 4); 543 544 /* 545 * Convert to physcal address. We cannot access to Open Firmware's 546 * virtual address space. 547 */ 548 OF_call_method("translate", mmu, 1, 3, romfont, &romfont, &m, &e); 549 550 /* Get character size */ 551 OF_interpret("char-width", 1, &char_width); 552 OF_interpret("char-height", 1, &char_height); 553 554 openfirm6x11.name = "Open Firmware"; 555 openfirm6x11.firstchar = 32; 556 openfirm6x11.numchars = 96; 557 openfirm6x11.encoding = WSDISPLAY_FONTENC_ISO; 558 openfirm6x11.fontwidth = char_width; 559 openfirm6x11.fontheight = char_height; 560 openfirm6x11.stride = 1; 561 openfirm6x11.bitorder = WSDISPLAY_FONTORDER_L2R; 562 openfirm6x11.byteorder = WSDISPLAY_FONTORDER_L2R; 563 openfirm6x11.data = romfont; 564 565 return 0; 566 } 567 568 int 569 ofb_getcmap(sc, cm) 570 struct ofb_softc *sc; 571 struct wsdisplay_cmap *cm; 572 { 573 u_int index = cm->index; 574 u_int count = cm->count; 575 int error; 576 577 if (index >= 256 || count > 256 || index + count > 256) 578 return EINVAL; 579 580 error = copyout(&sc->sc_cmap_red[index], cm->red, count); 581 if (error) 582 return error; 583 error = copyout(&sc->sc_cmap_green[index], cm->green, count); 584 if (error) 585 return error; 586 error = copyout(&sc->sc_cmap_blue[index], cm->blue, count); 587 if (error) 588 return error; 589 590 return 0; 591 } 592 593 int 594 ofb_putcmap(sc, cm) 595 struct ofb_softc *sc; 596 struct wsdisplay_cmap *cm; 597 { 598 struct ofb_devconfig *dc = sc->sc_dc; 599 u_int index = cm->index; 600 u_int count = cm->count; 601 int i, error; 602 u_char rbuf[256], gbuf[256], bbuf[256]; 603 u_char *r, *g, *b; 604 605 if (cm->index >= 256 || cm->count > 256 || 606 (cm->index + cm->count) > 256) 607 return EINVAL; 608 error = copyin(cm->red, &rbuf[index], count); 609 if (error) 610 return error; 611 error = copyin(cm->green, &gbuf[index], count); 612 if (error) 613 return error; 614 error = copyin(cm->blue, &bbuf[index], count); 615 if (error) 616 return error; 617 618 memcpy(&sc->sc_cmap_red[index], &rbuf[index], count); 619 memcpy(&sc->sc_cmap_green[index], &gbuf[index], count); 620 memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count); 621 622 r = &sc->sc_cmap_red[index]; 623 g = &sc->sc_cmap_green[index]; 624 b = &sc->sc_cmap_blue[index]; 625 for (i = 0; i < count; i++) { 626 OF_call_method_1("color!", dc->dc_ih, 4, *r, *g, *b, index); 627 r++, g++, b++, index++; 628 } 629 return 0; 630 } 631