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