1 /* $NetBSD: tcx.c,v 1.4 2001/11/13 06:58:18 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 1996,1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Paul Kranenburg. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * color display (TCX) driver. 41 * 42 * Does not handle interrupts, even though they can occur. 43 * 44 * XXX should defer colormap updates to vertical retrace interrupts 45 */ 46 47 #include <sys/cdefs.h> 48 __KERNEL_RCSID(0, "$NetBSD: tcx.c,v 1.4 2001/11/13 06:58:18 lukem Exp $"); 49 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/buf.h> 53 #include <sys/device.h> 54 #include <sys/ioctl.h> 55 #include <sys/malloc.h> 56 #include <sys/mman.h> 57 #include <sys/tty.h> 58 #include <sys/conf.h> 59 60 #ifdef DEBUG 61 #include <sys/proc.h> 62 #include <sys/syslog.h> 63 #endif 64 65 #include <machine/bus.h> 66 #include <machine/autoconf.h> 67 68 #include <dev/sun/fbio.h> 69 #include <dev/sun/fbvar.h> 70 #include <dev/sun/btreg.h> 71 #include <dev/sun/btvar.h> 72 73 #include <dev/sbus/sbusvar.h> 74 #include <dev/sbus/tcxreg.h> 75 76 #include <machine/conf.h> 77 78 /* per-display variables */ 79 struct tcx_softc { 80 struct device sc_dev; /* base device */ 81 struct sbusdev sc_sd; /* sbus device */ 82 struct fbdevice sc_fb; /* frame buffer device */ 83 bus_space_tag_t sc_bustag; 84 struct sbus_reg sc_physadr[TCX_NREG]; /* phys addr of h/w */ 85 86 volatile struct bt_regs *sc_bt; /* Brooktree registers */ 87 volatile struct tcx_thc *sc_thc;/* THC registers */ 88 short sc_blanked; /* true if blanked */ 89 union bt_cmap sc_cmap; /* Brooktree color map */ 90 }; 91 92 /* autoconfiguration driver */ 93 static void tcxattach __P((struct device *, struct device *, void *)); 94 static int tcxmatch __P((struct device *, struct cfdata *, void *)); 95 static void tcx_unblank __P((struct device *)); 96 97 /* cdevsw prototypes */ 98 cdev_decl(tcx); 99 100 struct cfattach tcx_ca = { 101 sizeof(struct tcx_softc), tcxmatch, tcxattach 102 }; 103 104 extern struct cfdriver tcx_cd; 105 106 /* frame buffer generic driver */ 107 static struct fbdriver tcx_fbdriver = { 108 tcx_unblank, tcxopen, tcxclose, tcxioctl, tcxpoll, tcxmmap 109 }; 110 111 static void tcx_reset __P((struct tcx_softc *)); 112 static void tcx_loadcmap __P((struct tcx_softc *, int, int)); 113 114 #define OBPNAME "SUNW,tcx" 115 /* 116 * Match a tcx. 117 */ 118 int 119 tcxmatch(parent, cf, aux) 120 struct device *parent; 121 struct cfdata *cf; 122 void *aux; 123 { 124 struct sbus_attach_args *sa = aux; 125 126 return (strcmp(sa->sa_name, OBPNAME) == 0); 127 } 128 129 /* 130 * Attach a display. 131 */ 132 void 133 tcxattach(parent, self, args) 134 struct device *parent, *self; 135 void *args; 136 { 137 struct tcx_softc *sc = (struct tcx_softc *)self; 138 struct sbus_attach_args *sa = args; 139 int node, ramsize; 140 volatile struct bt_regs *bt; 141 struct fbdevice *fb = &sc->sc_fb; 142 bus_space_handle_t bh; 143 int isconsole; 144 145 sc->sc_bustag = sa->sa_bustag; 146 node = sa->sa_node; 147 148 fb->fb_driver = &tcx_fbdriver; 149 fb->fb_device = &sc->sc_dev; 150 /* Mask out invalid flags from the user. */ 151 fb->fb_flags = sc->sc_dev.dv_cfdata->cf_flags & FB_USERMASK; 152 fb->fb_type.fb_depth = node_has_property(node, "tcx-24-bit") 153 ? 24 154 : (node_has_property(node, "tcx-8-bit") 155 ? 8 156 : 8); 157 158 fb_setsize_obp(fb, fb->fb_type.fb_depth, 1152, 900, node); 159 160 ramsize = fb->fb_type.fb_height * fb->fb_linebytes; 161 fb->fb_type.fb_cmsize = 256; 162 fb->fb_type.fb_size = ramsize; 163 printf(": %s, %d x %d", OBPNAME, 164 fb->fb_type.fb_width, 165 fb->fb_type.fb_height); 166 167 /* 168 * XXX - should be set to FBTYPE_TCX. 169 * XXX For CG3 emulation to work in current (96/6) X11 servers, 170 * XXX `fbtype' must point to an "unregocnised" entry. 171 */ 172 fb->fb_type.fb_type = FBTYPE_RESERVED3; 173 174 175 if (sa->sa_nreg != TCX_NREG) { 176 printf("%s: only %d register sets\n", 177 self->dv_xname, sa->sa_nreg); 178 return; 179 } 180 bcopy(sa->sa_reg, sc->sc_physadr, 181 sa->sa_nreg * sizeof(struct sbus_reg)); 182 183 /* XXX - fix THC and TEC offsets */ 184 sc->sc_physadr[TCX_REG_TEC].sbr_offset += 0x1000; 185 sc->sc_physadr[TCX_REG_THC].sbr_offset += 0x1000; 186 187 /* Map the register banks we care about */ 188 if (sbus_bus_map(sa->sa_bustag, 189 (bus_type_t)sc->sc_physadr[TCX_REG_THC].sbr_slot, 190 (bus_addr_t)sc->sc_physadr[TCX_REG_THC].sbr_offset, 191 sizeof (struct tcx_thc), 192 BUS_SPACE_MAP_LINEAR, 193 0, &bh) != 0) { 194 printf("tcxattach: cannot map thc registers\n"); 195 return; 196 } 197 sc->sc_thc = (volatile struct tcx_thc *)bh; 198 199 if (sbus_bus_map(sa->sa_bustag, 200 (bus_type_t)sc->sc_physadr[TCX_REG_CMAP].sbr_slot, 201 (bus_addr_t)sc->sc_physadr[TCX_REG_CMAP].sbr_offset, 202 sizeof (struct bt_regs), 203 BUS_SPACE_MAP_LINEAR, 204 0, &bh) != 0) { 205 printf("tcxattach: cannot map bt registers\n"); 206 return; 207 } 208 sc->sc_bt = bt = (volatile struct bt_regs *)bh; 209 210 isconsole = fb_is_console(node); 211 212 printf(", id %d, rev %d, sense %d", 213 (sc->sc_thc->thc_config & THC_CFG_FBID) >> THC_CFG_FBID_SHIFT, 214 (sc->sc_thc->thc_config & THC_CFG_REV) >> THC_CFG_REV_SHIFT, 215 (sc->sc_thc->thc_config & THC_CFG_SENSE) >> THC_CFG_SENSE_SHIFT 216 ); 217 218 /* reset cursor & frame buffer controls */ 219 tcx_reset(sc); 220 221 /* Initialize the default color map. */ 222 bt_initcmap(&sc->sc_cmap, 256); 223 tcx_loadcmap(sc, 0, 256); 224 225 /* enable video */ 226 sc->sc_thc->thc_hcmisc |= THC_MISC_VIDEN; 227 228 if (isconsole) { 229 printf(" (console)\n"); 230 } else 231 printf("\n"); 232 233 sbus_establish(&sc->sc_sd, &sc->sc_dev); 234 fb_attach(&sc->sc_fb, isconsole); 235 } 236 237 int 238 tcxopen(dev, flags, mode, p) 239 dev_t dev; 240 int flags, mode; 241 struct proc *p; 242 { 243 int unit = minor(dev); 244 245 if (unit >= tcx_cd.cd_ndevs || tcx_cd.cd_devs[unit] == NULL) 246 return (ENXIO); 247 return (0); 248 } 249 250 int 251 tcxclose(dev, flags, mode, p) 252 dev_t dev; 253 int flags, mode; 254 struct proc *p; 255 { 256 struct tcx_softc *sc = tcx_cd.cd_devs[minor(dev)]; 257 258 tcx_reset(sc); 259 return (0); 260 } 261 262 int 263 tcxioctl(dev, cmd, data, flags, p) 264 dev_t dev; 265 u_long cmd; 266 caddr_t data; 267 int flags; 268 struct proc *p; 269 { 270 struct tcx_softc *sc = tcx_cd.cd_devs[minor(dev)]; 271 int error; 272 273 switch (cmd) { 274 275 case FBIOGTYPE: 276 *(struct fbtype *)data = sc->sc_fb.fb_type; 277 break; 278 279 case FBIOGATTR: 280 #define fba ((struct fbgattr *)data) 281 fba->real_type = sc->sc_fb.fb_type.fb_type; 282 fba->owner = 0; /* XXX ??? */ 283 fba->fbtype = sc->sc_fb.fb_type; 284 fba->sattr.flags = 0; 285 fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type; 286 fba->sattr.dev_specific[0] = -1; 287 fba->emu_types[0] = sc->sc_fb.fb_type.fb_type; 288 fba->emu_types[1] = FBTYPE_SUN3COLOR; 289 fba->emu_types[2] = -1; 290 #undef fba 291 break; 292 293 case FBIOGETCMAP: 294 #define p ((struct fbcmap *)data) 295 return (bt_getcmap(p, &sc->sc_cmap, 256, 1)); 296 297 case FBIOPUTCMAP: 298 /* copy to software map */ 299 error = bt_putcmap(p, &sc->sc_cmap, 256, 1); 300 if (error) 301 return (error); 302 /* now blast them into the chip */ 303 /* XXX should use retrace interrupt */ 304 tcx_loadcmap(sc, p->index, p->count); 305 #undef p 306 break; 307 308 case FBIOGVIDEO: 309 *(int *)data = sc->sc_blanked; 310 break; 311 312 case FBIOSVIDEO: 313 if (*(int *)data) 314 tcx_unblank(&sc->sc_dev); 315 else if (!sc->sc_blanked) { 316 sc->sc_blanked = 1; 317 sc->sc_thc->thc_hcmisc &= ~THC_MISC_VIDEN; 318 /* Put monitor in `power-saving mode' */ 319 sc->sc_thc->thc_hcmisc |= THC_MISC_VSYNC_DISABLE; 320 sc->sc_thc->thc_hcmisc |= THC_MISC_HSYNC_DISABLE; 321 } 322 break; 323 324 default: 325 #ifdef DEBUG 326 log(LOG_NOTICE, "tcxioctl(0x%lx) (%s[%d])\n", cmd, 327 p->p_comm, p->p_pid); 328 #endif 329 return (ENOTTY); 330 } 331 return (0); 332 } 333 334 int 335 tcxpoll(dev, events, p) 336 dev_t dev; 337 int events; 338 struct proc *p; 339 { 340 341 return (seltrue(dev, events, p)); 342 } 343 344 /* 345 * Clean up hardware state (e.g., after bootup or after X crashes). 346 */ 347 static void 348 tcx_reset(sc) 349 struct tcx_softc *sc; 350 { 351 volatile struct bt_regs *bt; 352 353 /* Enable cursor in Brooktree DAC. */ 354 bt = sc->sc_bt; 355 bt->bt_addr = 0x06 << 24; 356 bt->bt_ctrl |= 0x03 << 24; 357 } 358 359 /* 360 * Load a subset of the current (new) colormap into the color DAC. 361 */ 362 static void 363 tcx_loadcmap(sc, start, ncolors) 364 struct tcx_softc *sc; 365 int start, ncolors; 366 { 367 volatile struct bt_regs *bt; 368 u_int *ip, i; 369 int count; 370 371 ip = &sc->sc_cmap.cm_chip[BT_D4M3(start)]; /* start/4 * 3 */ 372 count = BT_D4M3(start + ncolors - 1) - BT_D4M3(start) + 3; 373 bt = sc->sc_bt; 374 bt->bt_addr = BT_D4M4(start) << 24; 375 while (--count >= 0) { 376 i = *ip++; 377 /* hardware that makes one want to pound boards with hammers */ 378 bt->bt_cmap = i; 379 bt->bt_cmap = i << 8; 380 bt->bt_cmap = i << 16; 381 bt->bt_cmap = i << 24; 382 } 383 } 384 385 static void 386 tcx_unblank(dev) 387 struct device *dev; 388 { 389 struct tcx_softc *sc = (struct tcx_softc *)dev; 390 391 if (sc->sc_blanked) { 392 sc->sc_blanked = 0; 393 sc->sc_thc->thc_hcmisc &= ~THC_MISC_VSYNC_DISABLE; 394 sc->sc_thc->thc_hcmisc &= ~THC_MISC_HSYNC_DISABLE; 395 sc->sc_thc->thc_hcmisc |= THC_MISC_VIDEN; 396 } 397 } 398 399 /* 400 * Base addresses at which users can mmap() the various pieces of a tcx. 401 */ 402 #define TCX_USER_RAM 0x00000000 403 #define TCX_USER_RAM24 0x01000000 404 #define TCX_USER_RAM_COMPAT 0x04000000 /* cg3 emulation */ 405 #define TCX_USER_STIP 0x10000000 406 #define TCX_USER_BLIT 0x20000000 407 #define TCX_USER_RDFB32 0x28000000 408 #define TCX_USER_RSTIP 0x30000000 409 #define TCX_USER_RBLIT 0x38000000 410 #define TCX_USER_TEC 0x70001000 411 #define TCX_USER_BTREGS 0x70002000 412 #define TCX_USER_THC 0x70004000 413 #define TCX_USER_DHC 0x70008000 414 #define TCX_USER_ALT 0x7000a000 415 #define TCX_USER_UART 0x7000c000 416 #define TCX_USER_VRT 0x7000e000 417 #define TCX_USER_ROM 0x70010000 418 419 struct mmo { 420 u_int mo_uaddr; /* user (virtual) address */ 421 u_int mo_size; /* size, or 0 for video ram size */ 422 u_int mo_bank; /* register bank number */ 423 }; 424 425 /* 426 * Return the address that would map the given device at the given 427 * offset, allowing for the given protection, or return -1 for error. 428 * 429 * XXX needs testing against `demanding' applications (e.g., aviator) 430 */ 431 paddr_t 432 tcxmmap(dev, off, prot) 433 dev_t dev; 434 off_t off; 435 int prot; 436 { 437 struct tcx_softc *sc = tcx_cd.cd_devs[minor(dev)]; 438 struct sbus_reg *rr = sc->sc_physadr; 439 struct mmo *mo; 440 u_int u, sz; 441 static struct mmo mmo[] = { 442 { TCX_USER_RAM, 0, TCX_REG_DFB8 }, 443 { TCX_USER_RAM24, 0, TCX_REG_DFB24 }, 444 { TCX_USER_RAM_COMPAT, 0, TCX_REG_DFB8 }, 445 446 { TCX_USER_STIP, 1, TCX_REG_STIP }, 447 { TCX_USER_BLIT, 1, TCX_REG_BLIT }, 448 { TCX_USER_RDFB32, 1, TCX_REG_RDFB32 }, 449 { TCX_USER_RSTIP, 1, TCX_REG_RSTIP }, 450 { TCX_USER_RBLIT, 1, TCX_REG_RBLIT }, 451 { TCX_USER_TEC, 1, TCX_REG_TEC }, 452 { TCX_USER_BTREGS, 8192 /* XXX */, TCX_REG_CMAP }, 453 { TCX_USER_THC, sizeof(struct tcx_thc), TCX_REG_THC }, 454 { TCX_USER_DHC, 1, TCX_REG_DHC }, 455 { TCX_USER_ALT, 1, TCX_REG_ALT }, 456 { TCX_USER_ROM, 65536, TCX_REG_ROM }, 457 }; 458 #define NMMO (sizeof mmo / sizeof *mmo) 459 460 if (off & PGOFSET) 461 panic("tcxmmap"); 462 463 /* 464 * Entries with size 0 map video RAM (i.e., the size in fb data). 465 * 466 * Since we work in pages, the fact that the map offset table's 467 * sizes are sometimes bizarre (e.g., 1) is effectively ignored: 468 * one byte is as good as one page. 469 */ 470 for (mo = mmo; mo < &mmo[NMMO]; mo++) { 471 if ((u_int)off < mo->mo_uaddr) 472 continue; 473 u = off - mo->mo_uaddr; 474 sz = mo->mo_size ? mo->mo_size : sc->sc_fb.fb_type.fb_size; 475 if (u < sz) { 476 bus_type_t t = (bus_type_t)rr[mo->mo_bank].sbr_slot; 477 bus_addr_t a = BUS_ADDR(t, rr[mo->mo_bank].sbr_offset); 478 479 return (bus_space_mmap(sc->sc_bustag, 480 a, 481 u, 482 prot, 483 BUS_SPACE_MAP_LINEAR)); 484 } 485 } 486 return (-1); 487 } 488