1 /* 2 * Copyright (c) 1993 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This software was developed by the Computer Systems Engineering group 6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7 * contributed to Berkeley. 8 * 9 * All advertising materials mentioning features or use of this software 10 * must display the following acknowledgement: 11 * This product includes software developed by the University of 12 * California, Lawrence Berkeley Laboratory. 13 * 14 * %sccs.include.redist.c% 15 * 16 * @(#)cgsix.c 8.2 (Berkeley) 12/12/93 17 * 18 * from: $Header: cgsix.c,v 1.2 93/10/18 00:01:51 torek Exp $ 19 */ 20 21 /* 22 * color display (cgsix) driver. 23 * 24 * Does not handle interrupts, even though they can occur. 25 * 26 * XXX should defer colormap updates to vertical retrace interrupts 27 */ 28 29 #include <sys/param.h> 30 #include <sys/buf.h> 31 #include <sys/device.h> 32 #include <sys/fbio.h> 33 #include <sys/ioctl.h> 34 #include <sys/malloc.h> 35 #include <sys/mman.h> 36 #include <sys/tty.h> 37 38 #ifdef DEBUG 39 #include <sys/proc.h> 40 #include <sys/syslog.h> 41 #endif 42 43 #include <machine/autoconf.h> 44 #include <machine/pmap.h> 45 #include <machine/fbvar.h> 46 47 #include <sparc/sbus/btreg.h> 48 #include <sparc/sbus/btvar.h> 49 #include <sparc/sbus/cgsixreg.h> 50 #include <sparc/sbus/sbusvar.h> 51 52 union cursor_cmap { /* colormap, like bt_cmap, but tiny */ 53 u_char cm_map[2][3]; /* 2 R/G/B entries */ 54 u_int cm_chip[2]; /* 2 chip equivalents */ 55 }; 56 57 struct cg6_cursor { /* cg6 hardware cursor status */ 58 short cc_enable; /* cursor is enabled */ 59 struct fbcurpos cc_pos; /* position */ 60 struct fbcurpos cc_hot; /* hot-spot */ 61 struct fbcurpos cc_size; /* size of mask & image fields */ 62 u_int cc_bits[2][32]; /* space for mask & image bits */ 63 union cursor_cmap cc_color; /* cursor colormap */ 64 }; 65 66 /* per-display variables */ 67 struct cgsix_softc { 68 struct device sc_dev; /* base device */ 69 struct sbusdev sc_sd; /* sbus device */ 70 struct fbdevice sc_fb; /* frame buffer device */ 71 volatile struct cg6_layout *sc_physadr; /* phys addr of h/w */ 72 volatile struct bt_regs *sc_bt; /* Brooktree registers */ 73 volatile int *sc_fhc; /* FHC register */ 74 volatile struct cg6_thc *sc_thc; /* THC registers */ 75 int sc_blanked; /* true if blanked */ 76 struct cg6_cursor sc_cursor; /* software cursor info */ 77 union bt_cmap sc_cmap; /* Brooktree color map */ 78 }; 79 80 /* autoconfiguration driver */ 81 static void cgsixattach(struct device *, struct device *, void *); 82 struct cfdriver cgsixcd = 83 { NULL, "cgsix", matchbyname, cgsixattach, 84 DV_DULL, sizeof(struct cgsix_softc) }; 85 86 /* frame buffer generic driver */ 87 static void cg6_unblank(struct device *); 88 static struct fbdriver cg6_fbdriver = { cg6_unblank }; 89 90 /* 91 * Unlike the bw2 and cg3 drivers, we do not need to provide an rconsole 92 * interface, as the cg6 is fast enough. 93 */ 94 95 extern int fbnode; 96 97 #define CGSIX_MAJOR 67 /* XXX */ 98 99 static void cg6_reset __P((struct cgsix_softc *)); 100 static void cg6_loadcmap __P((struct cgsix_softc *, int, int)); 101 static void cg6_loadomap __P((struct cgsix_softc *)); 102 static void cg6_setcursor __P((struct cgsix_softc *));/* set position */ 103 static void cg6_loadcursor __P((struct cgsix_softc *));/* set shape */ 104 105 /* 106 * Attach a display. 107 */ 108 void 109 cgsixattach(parent, self, args) 110 struct device *parent, *self; 111 void *args; 112 { 113 register struct cgsix_softc *sc = (struct cgsix_softc *)self; 114 register struct sbus_attach_args *sa = args; 115 register int node = sa->sa_ra.ra_node, ramsize, i; 116 register volatile struct bt_regs *bt; 117 register volatile struct cg6_layout *p; 118 119 sc->sc_fb.fb_major = CGSIX_MAJOR; /* XXX to be removed */ 120 121 sc->sc_fb.fb_driver = &cg6_fbdriver; 122 sc->sc_fb.fb_device = &sc->sc_dev; 123 sc->sc_fb.fb_type.fb_type = FBTYPE_SUNFAST_COLOR; 124 sc->sc_fb.fb_type.fb_width = getpropint(node, "width", 1152); 125 sc->sc_fb.fb_type.fb_height = getpropint(node, "height", 900); 126 sc->sc_fb.fb_linebytes = getpropint(node, "linebytes", 1152); 127 ramsize = sc->sc_fb.fb_type.fb_height * sc->sc_fb.fb_linebytes; 128 sc->sc_fb.fb_type.fb_depth = 8; 129 sc->sc_fb.fb_type.fb_cmsize = 256; 130 sc->sc_fb.fb_type.fb_size = ramsize; 131 printf(": %s, %d x %d", getpropstring(node, "model"), 132 sc->sc_fb.fb_type.fb_width, sc->sc_fb.fb_type.fb_height); 133 134 /* 135 * Dunno what the PROM has mapped, though obviously it must have 136 * the video RAM mapped. Just map what we care about for ourselves 137 * (the FHC, THC, and Brooktree registers). 138 */ 139 sc->sc_physadr = p = (struct cg6_layout *)sa->sa_ra.ra_paddr; 140 sc->sc_bt = bt = (volatile struct bt_regs *) 141 mapiodev((caddr_t)&p->cg6_bt_un.un_btregs, sizeof(struct bt_regs)); 142 sc->sc_fhc = (volatile int *) 143 mapiodev((caddr_t)&p->cg6_fhc_un.un_fhc, sizeof(int)); 144 sc->sc_thc = (volatile struct cg6_thc *) 145 mapiodev((caddr_t)&p->cg6_thc_un.un_thc, sizeof(struct cg6_thc)); 146 147 /* reset cursor & frame buffer controls */ 148 cg6_reset(sc); 149 150 /* grab initial (current) color map (DOES THIS WORK?) */ 151 bt->bt_addr = 0; 152 for (i = 0; i < 256 * 3; i++) 153 ((char *)&sc->sc_cmap)[i] = bt->bt_cmap >> 24; 154 155 /* enable video */ 156 sc->sc_thc->thc_misc |= THC_MISC_VIDEN; 157 158 printf("\n"); 159 sbus_establish(&sc->sc_sd, &sc->sc_dev); 160 if (node == fbnode) 161 fb_attach(&sc->sc_fb); 162 } 163 164 int 165 cgsixopen(dev, flags, mode, p) 166 dev_t dev; 167 int flags, mode; 168 struct proc *p; 169 { 170 int unit = minor(dev); 171 172 if (unit >= cgsixcd.cd_ndevs || cgsixcd.cd_devs[unit] == NULL) 173 return (ENXIO); 174 return (0); 175 } 176 177 int 178 cgsixclose(dev, flags, mode, p) 179 dev_t dev; 180 int flags, mode; 181 struct proc *p; 182 { 183 struct cgsix_softc *sc = cgsixcd.cd_devs[minor(dev)]; 184 185 cg6_reset(sc); 186 return (0); 187 } 188 189 int 190 cgsixioctl(dev, cmd, data, flags, p) 191 dev_t dev; 192 int cmd; 193 register caddr_t data; 194 int flags; 195 struct proc *p; 196 { 197 register struct cgsix_softc *sc = cgsixcd.cd_devs[minor(dev)]; 198 u_int count; 199 int i, v, error; 200 union cursor_cmap tcm; 201 202 switch (cmd) { 203 204 case FBIOGTYPE: 205 *(struct fbtype *)data = sc->sc_fb.fb_type; 206 break; 207 208 case FBIOGATTR: 209 #define fba ((struct fbgattr *)data) 210 fba->real_type = sc->sc_fb.fb_type.fb_type; 211 fba->owner = 0; /* XXX ??? */ 212 fba->fbtype = sc->sc_fb.fb_type; 213 fba->sattr.flags = 0; 214 fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type; 215 fba->sattr.dev_specific[0] = -1; 216 fba->emu_types[0] = sc->sc_fb.fb_type.fb_type; 217 fba->emu_types[1] = -1; 218 #undef fba 219 break; 220 221 case FBIOGETCMAP: 222 return (bt_getcmap((struct fbcmap *)data, &sc->sc_cmap, 256)); 223 224 case FBIOPUTCMAP: 225 /* copy to software map */ 226 #define p ((struct fbcmap *)data) 227 error = bt_putcmap(p, &sc->sc_cmap, 256); 228 if (error) 229 return (error); 230 /* now blast them into the chip */ 231 /* XXX should use retrace interrupt */ 232 cg6_loadcmap(sc, p->index, p->count); 233 #undef p 234 break; 235 236 case FBIOGVIDEO: 237 *(int *)data = sc->sc_blanked; 238 break; 239 240 case FBIOSVIDEO: 241 if (*(int *)data) 242 cg6_unblank(&sc->sc_dev); 243 else if (!sc->sc_blanked) { 244 sc->sc_blanked = 1; 245 sc->sc_thc->thc_misc &= ~THC_MISC_VIDEN; 246 } 247 break; 248 249 /* these are for both FBIOSCURSOR and FBIOGCURSOR */ 250 #define p ((struct fbcursor *)data) 251 #define cc (&sc->sc_cursor) 252 253 case FBIOGCURSOR: 254 /* do not quite want everything here... */ 255 p->set = FB_CUR_SETALL; /* close enough, anyway */ 256 p->enable = cc->cc_enable; 257 p->pos = cc->cc_pos; 258 p->hot = cc->cc_hot; 259 p->size = cc->cc_size; 260 261 /* begin ugh ... can we lose some of this crap?? */ 262 if (p->image != NULL) { 263 count = cc->cc_size.y * 32 / NBBY; 264 error = copyout((caddr_t)cc->cc_bits[1], 265 (caddr_t)p->image, count); 266 if (error) 267 return (error); 268 error = copyout((caddr_t)cc->cc_bits[0], 269 (caddr_t)p->mask, count); 270 if (error) 271 return (error); 272 } 273 if (p->cmap.red != NULL) { 274 error = bt_getcmap(&p->cmap, 275 (union bt_cmap *)&cc->cc_color, 2); 276 if (error) 277 return (error); 278 } else { 279 p->cmap.index = 0; 280 p->cmap.count = 2; 281 } 282 /* end ugh */ 283 break; 284 285 case FBIOSCURSOR: 286 /* 287 * For setcmap and setshape, verify parameters, so that 288 * we do not get halfway through an update and then crap 289 * out with the software state screwed up. 290 */ 291 v = p->set; 292 if (v & FB_CUR_SETCMAP) { 293 /* 294 * This use of a temporary copy of the cursor 295 * colormap is not terribly efficient, but these 296 * copies are small (8 bytes)... 297 */ 298 tcm = cc->cc_color; 299 error = bt_putcmap(&p->cmap, (union bt_cmap *)&tcm, 2); 300 if (error) 301 return (error); 302 } 303 if (v & FB_CUR_SETSHAPE) { 304 if ((u_int)p->size.x > 32 || (u_int)p->size.y > 32) 305 return (EINVAL); 306 count = p->size.y * 32 / NBBY; 307 if (!useracc(p->image, count, B_READ) || 308 !useracc(p->mask, count, B_READ)) 309 return (EFAULT); 310 } 311 312 /* parameters are OK; do it */ 313 if (v & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)) { 314 if (v & FB_CUR_SETCUR) 315 cc->cc_enable = p->enable; 316 if (v & FB_CUR_SETPOS) 317 cc->cc_pos = p->pos; 318 if (v & FB_CUR_SETHOT) 319 cc->cc_hot = p->hot; 320 cg6_setcursor(sc); 321 } 322 if (v & FB_CUR_SETCMAP) { 323 cc->cc_color = tcm; 324 cg6_loadomap(sc); /* XXX defer to vertical retrace */ 325 } 326 if (v & FB_CUR_SETSHAPE) { 327 cc->cc_size = p->size; 328 count = p->size.y * 32 / NBBY; 329 bzero((caddr_t)cc->cc_bits, sizeof cc->cc_bits); 330 bcopy(p->mask, (caddr_t)cc->cc_bits[0], count); 331 bcopy(p->image, (caddr_t)cc->cc_bits[1], count); 332 cg6_loadcursor(sc); 333 } 334 break; 335 336 #undef p 337 #undef cc 338 339 case FBIOGCURPOS: 340 *(struct fbcurpos *)data = sc->sc_cursor.cc_pos; 341 break; 342 343 case FBIOSCURPOS: 344 sc->sc_cursor.cc_pos = *(struct fbcurpos *)data; 345 cg6_setcursor(sc); 346 break; 347 348 case FBIOGCURMAX: 349 /* max cursor size is 32x32 */ 350 ((struct fbcurpos *)data)->x = 32; 351 ((struct fbcurpos *)data)->y = 32; 352 break; 353 354 default: 355 #ifdef DEBUG 356 log(LOG_NOTICE, "cgsixioctl(%x) (%s[%d])\n", cmd, 357 p->p_comm, p->p_pid); 358 #endif 359 return (ENOTTY); 360 } 361 return (0); 362 } 363 364 /* 365 * Clean up hardware state (e.g., after bootup or after X crashes). 366 */ 367 static void 368 cg6_reset(sc) 369 register struct cgsix_softc *sc; 370 { 371 register int oldfhc, newfhc, rev; 372 register volatile struct bt_regs *bt; 373 374 /* hide the cursor, just in case */ 375 sc->sc_thc->thc_cursxy = (THC_CURSOFF << 16) | THC_CURSOFF; 376 377 /* take care of hardware bugs in various revisions */ 378 oldfhc = *sc->sc_fhc; 379 rev = (oldfhc >> FHC_REV_SHIFT) & (FHC_REV_MASK >> FHC_REV_SHIFT); 380 if (rev < 5) { 381 /* 382 * Keep current resolution; set cpu to 68020, set test 383 * window (size 1Kx1K), and for rev 1, disable dest cache. 384 */ 385 newfhc = (oldfhc & FHC_RES_MASK) | FHC_CPU_68020 | FHC_TEST | 386 (11 << FHC_TESTX_SHIFT) | (11 << FHC_TESTY_SHIFT); 387 if (rev < 2) 388 newfhc |= FHC_DST_DISABLE; 389 *sc->sc_fhc = newfhc; 390 } 391 392 /* Enable cursor in Brooktree DAC. */ 393 bt = sc->sc_bt; 394 bt->bt_addr = 0x06 << 24; 395 bt->bt_ctrl |= 0x03 << 24; 396 } 397 398 static void 399 cg6_setcursor(sc) 400 register struct cgsix_softc *sc; 401 { 402 403 /* we need to subtract the hot-spot value here */ 404 #define COORD(f) (sc->sc_cursor.cc_pos.f - sc->sc_cursor.cc_hot.f) 405 sc->sc_thc->thc_cursxy = sc->sc_cursor.cc_enable ? 406 ((COORD(x) << 16) | (COORD(y) & 0xffff)) : 407 (THC_CURSOFF << 16) | THC_CURSOFF; 408 #undef COORD 409 } 410 411 static void 412 cg6_loadcursor(sc) 413 register struct cgsix_softc *sc; 414 { 415 register volatile struct cg6_thc *thc; 416 register u_int edgemask, m; 417 register int i; 418 419 /* 420 * Keep the top size.x bits. Here we *throw out* the top 421 * size.x bits from an all-one-bits word, introducing zeros in 422 * the top size.x bits, then invert all the bits to get what 423 * we really wanted as our mask. But this fails if size.x is 424 * 32---a sparc uses only the low 5 bits of the shift count--- 425 * so we have to special case that. 426 */ 427 edgemask = ~0; 428 if (sc->sc_cursor.cc_size.x < 32) 429 edgemask = ~(edgemask >> sc->sc_cursor.cc_size.x); 430 thc = sc->sc_thc; 431 for (i = 0; i < 32; i++) { 432 m = sc->sc_cursor.cc_bits[0][i] & edgemask; 433 thc->thc_cursmask[i] = m; 434 thc->thc_cursbits[i] = m & sc->sc_cursor.cc_bits[1][i]; 435 } 436 } 437 438 /* 439 * Load a subset of the current (new) colormap into the color DAC. 440 */ 441 static void 442 cg6_loadcmap(sc, start, ncolors) 443 register struct cgsix_softc *sc; 444 register int start, ncolors; 445 { 446 register volatile struct bt_regs *bt; 447 register u_int *ip, i; 448 register int count; 449 450 ip = &sc->sc_cmap.cm_chip[BT_D4M3(start)]; /* start/4 * 3 */ 451 count = BT_D4M3(start + ncolors - 1) - BT_D4M3(start) + 3; 452 bt = sc->sc_bt; 453 bt->bt_addr = BT_D4M4(start) << 24; 454 while (--count >= 0) { 455 i = *ip++; 456 /* hardware that makes one want to pound boards with hammers */ 457 bt->bt_cmap = i; 458 bt->bt_cmap = i << 8; 459 bt->bt_cmap = i << 16; 460 bt->bt_cmap = i << 24; 461 } 462 } 463 464 /* 465 * Load the cursor (overlay `foreground' and `background') colors. 466 */ 467 static void 468 cg6_loadomap(sc) 469 register struct cgsix_softc *sc; 470 { 471 register volatile struct bt_regs *bt; 472 register u_int i; 473 474 bt = sc->sc_bt; 475 bt->bt_addr = 0x01 << 24; /* set background color */ 476 i = sc->sc_cursor.cc_color.cm_chip[0]; 477 bt->bt_omap = i; /* R */ 478 bt->bt_omap = i << 8; /* G */ 479 bt->bt_omap = i << 16; /* B */ 480 481 bt->bt_addr = 0x03 << 24; /* set foreground color */ 482 bt->bt_omap = i << 24; /* R */ 483 i = sc->sc_cursor.cc_color.cm_chip[1]; 484 bt->bt_omap = i; /* G */ 485 bt->bt_omap = i << 8; /* B */ 486 } 487 488 static void 489 cg6_unblank(dev) 490 struct device *dev; 491 { 492 struct cgsix_softc *sc = (struct cgsix_softc *)dev; 493 494 if (sc->sc_blanked) { 495 sc->sc_blanked = 0; 496 sc->sc_thc->thc_misc |= THC_MISC_VIDEN; 497 } 498 } 499 500 /* XXX the following should be moved to a "user interface" header */ 501 /* 502 * Base addresses at which users can mmap() the various pieces of a cg6. 503 * Note that although the Brooktree color registers do not occupy 8K, 504 * the X server dies if we do not allow it to map 8K there (it just maps 505 * from 0x70000000 forwards, as a contiguous chunk). 506 */ 507 #define CG6_USER_FBC 0x70000000 508 #define CG6_USER_TEC 0x70001000 509 #define CG6_USER_BTREGS 0x70002000 510 #define CG6_USER_FHC 0x70004000 511 #define CG6_USER_THC 0x70005000 512 #define CG6_USER_ROM 0x70006000 513 #define CG6_USER_RAM 0x70016000 514 #define CG6_USER_DHC 0x80000000 515 516 struct mmo { 517 u_int mo_uaddr; /* user (virtual) address */ 518 u_int mo_size; /* size, or 0 for video ram size */ 519 u_int mo_physoff; /* offset from sc_physadr */ 520 }; 521 522 /* 523 * Return the address that would map the given device at the given 524 * offset, allowing for the given protection, or return -1 for error. 525 * 526 * XXX needs testing against `demanding' applications (e.g., aviator) 527 */ 528 int 529 cgsixmap(dev, off, prot) 530 dev_t dev; 531 int off, prot; 532 { 533 register struct cgsix_softc *sc = cgsixcd.cd_devs[minor(dev)]; 534 register struct mmo *mo; 535 register u_int u, sz; 536 #define O(memb) ((u_int)(&((struct cg6_layout *)0)->memb)) 537 static struct mmo mmo[] = { 538 { CG6_USER_RAM, 0, O(cg6_ram) }, 539 540 /* do not actually know how big most of these are! */ 541 { CG6_USER_FBC, 1, O(cg6_fbc_un) }, 542 { CG6_USER_TEC, 1, O(cg6_tec_un) }, 543 { CG6_USER_BTREGS, 8192 /* XXX */, O(cg6_bt_un) }, 544 { CG6_USER_FHC, 1, O(cg6_fhc_un) }, 545 { CG6_USER_THC, sizeof(struct cg6_thc), O(cg6_thc_un) }, 546 { CG6_USER_ROM, 65536, O(cg6_rom_un) }, 547 { CG6_USER_DHC, 1, O(cg6_dhc_un) }, 548 }; 549 #define NMMO (sizeof mmo / sizeof *mmo) 550 551 if (off & PGOFSET) 552 panic("cgsixmap"); 553 554 /* 555 * Entries with size 0 map video RAM (i.e., the size in fb data). 556 * 557 * Since we work in pages, the fact that the map offset table's 558 * sizes are sometimes bizarre (e.g., 1) is effectively ignored: 559 * one byte is as good as one page. 560 */ 561 for (mo = mmo; mo < &mmo[NMMO]; mo++) { 562 if ((u_int)off < mo->mo_uaddr) 563 continue; 564 u = off - mo->mo_uaddr; 565 sz = mo->mo_size ? mo->mo_size : sc->sc_fb.fb_type.fb_size; 566 if (u < sz) 567 return ((int)sc->sc_physadr + u + mo->mo_physoff + 568 PMAP_OBIO + PMAP_NC); 569 } 570 #ifdef DEBUG 571 { 572 register struct proc *p = curproc; /* XXX */ 573 log(LOG_NOTICE, "cgsixmap(%x) (%s[%d])\n", off, p->p_comm, p->p_pid); 574 } 575 #endif 576 return (-1); /* not a user-map offset */ 577 } 578