1 /* $NetBSD: p9100.c,v 1.7 2001/12/08 19:42:45 cyber Exp $ */ 2 3 /*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matt Thomas. 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 (p9100) 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: p9100.c,v 1.7 2001/12/08 19:42:45 cyber 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 #include <machine/bus.h> 61 #include <machine/autoconf.h> 62 63 #include <dev/sun/fbio.h> 64 #include <dev/sun/fbvar.h> 65 #include <dev/sun/btreg.h> 66 #include <dev/sun/btvar.h> 67 #if 0 68 #include <dev/sbus/p9100reg.h> 69 #endif 70 71 #include <dev/sbus/sbusvar.h> 72 73 #include "tctrl.h" 74 #if NTCTRL > 0 75 #include <machine/tctrl.h> 76 #include <sparc/dev/tctrlvar.h>/*XXX*/ 77 #endif 78 79 #include <machine/conf.h> 80 81 /* per-display variables */ 82 struct p9100_softc { 83 struct device sc_dev; /* base device */ 84 struct sbusdev sc_sd; /* sbus device */ 85 struct fbdevice sc_fb; /* frame buffer device */ 86 bus_space_tag_t sc_bustag; 87 bus_type_t sc_ctl_slot; /* phys address description */ 88 bus_addr_t sc_ctl_paddr; /* for device mmap() */ 89 bus_size_t sc_ctl_psize; /* for device mmap() */ 90 bus_space_handle_t sc_ctl_memh; /* bus space handle */ 91 bus_type_t sc_cmd_slot; /* phys address description */ 92 bus_addr_t sc_cmd_paddr; /* for device mmap() */ 93 bus_size_t sc_cmd_psize; /* for device mmap() */ 94 bus_space_handle_t sc_cmd_memh; /* bus space handle */ 95 bus_type_t sc_fb_slot; /* phys address description */ 96 bus_addr_t sc_fb_paddr; /* for device mmap() */ 97 bus_size_t sc_fb_psize; /* for device mmap() */ 98 bus_space_handle_t sc_fb_memh; /* bus space handle */ 99 uint32_t sc_junk; 100 101 union bt_cmap sc_cmap; /* Brooktree color map */ 102 }; 103 104 /* The Tadpole 3GX Technical Reference Manual lies. The ramdac registers 105 * are map in 4 byte increments, not 8. 106 */ 107 #define SCRN_RPNT_CTL_1 0x0138 /* Screen Respaint Timing Control 1 */ 108 #define VIDEO_ENABLED 0x00000020 109 #define PWRUP_CNFG 0x0194 /* Power Up Configuration */ 110 #define DAC_CMAP_WRIDX 0x0200 /* IBM RGB528 Palette Address (Write) */ 111 #define DAC_CMAP_DATA 0x0204 /* IBM RGB528 Palette Data */ 112 #define DAC_PXL_MASK 0x0208 /* IBM RGB528 Pixel Mask */ 113 #define DAC_CMAP_RDIDX 0x020c /* IBM RGB528 Palette Address (Read) */ 114 #define DAC_INDX_LO 0x0210 /* IBM RGB528 Index Low */ 115 #define DAC_INDX_HI 0x0214 /* IBM RGB528 Index High */ 116 #define DAC_INDX_DATA 0x0218 /* IBM RGB528 Index Data (Indexed Registers) */ 117 #define DAC_INDX_CTL 0x021c /* IBM RGB528 Index Control */ 118 119 /* autoconfiguration driver */ 120 static int p9100_sbus_match(struct device *, struct cfdata *, void *); 121 static void p9100_sbus_attach(struct device *, struct device *, void *); 122 123 static void p9100unblank(struct device *); 124 static void p9100_shutdown(void *); 125 126 /* cdevsw prototypes */ 127 cdev_decl(p9100); 128 129 struct cfattach pnozz_ca = { 130 sizeof(struct p9100_softc), p9100_sbus_match, p9100_sbus_attach 131 }; 132 133 extern struct cfdriver pnozz_cd; 134 135 /* frame buffer generic driver */ 136 static struct fbdriver p9100fbdriver = { 137 p9100unblank, p9100open, p9100close, p9100ioctl, p9100poll, 138 p9100mmap 139 }; 140 141 static void p9100loadcmap(struct p9100_softc *, int, int); 142 static void p9100_set_video(struct p9100_softc *, int); 143 static int p9100_get_video(struct p9100_softc *); 144 static uint32_t p9100_ctl_read_4(struct p9100_softc *, bus_size_t); 145 static void p9100_ctl_write_4(struct p9100_softc *, bus_size_t, uint32_t); 146 #if 0 147 static uint8_t p9100_ramdac_read(struct p9100_softc *, bus_size_t); 148 #endif 149 static void p9100_ramdac_write(struct p9100_softc *, bus_size_t, uint8_t); 150 151 /* 152 * Match a p9100. 153 */ 154 static int 155 p9100_sbus_match(struct device *parent, struct cfdata *cf, void *aux) 156 { 157 struct sbus_attach_args *sa = aux; 158 159 return (strcmp("p9100", sa->sa_name) == 0); 160 } 161 162 163 /* 164 * Attach a display. We need to notice if it is the console, too. 165 */ 166 static void 167 p9100_sbus_attach(struct device *parent, struct device *self, void *args) 168 { 169 struct p9100_softc *sc = (struct p9100_softc *)self; 170 struct sbus_attach_args *sa = args; 171 struct fbdevice *fb = &sc->sc_fb; 172 int isconsole; 173 int node; 174 int i; 175 176 /* Remember cookies for p9100_mmap() */ 177 sc->sc_bustag = sa->sa_bustag; 178 sc->sc_ctl_slot = (bus_type_t)sa->sa_reg[0].sbr_slot; 179 sc->sc_ctl_paddr = sbus_bus_addr(sa->sa_bustag, 180 sa->sa_reg[0].sbr_slot, sa->sa_reg[0].sbr_offset); 181 sc->sc_ctl_psize = (bus_size_t)sa->sa_reg[0].sbr_size; 182 183 sc->sc_cmd_slot = (bus_type_t)sa->sa_reg[1].sbr_slot; 184 sc->sc_cmd_paddr = sbus_bus_addr(sa->sa_bustag, 185 sa->sa_reg[1].sbr_slot, sa->sa_reg[1].sbr_offset); 186 sc->sc_cmd_psize = (bus_size_t)sa->sa_reg[1].sbr_size; 187 188 sc->sc_fb_slot = (bus_type_t)sa->sa_reg[2].sbr_slot; 189 sc->sc_fb_paddr = sbus_bus_addr(sa->sa_bustag, 190 sa->sa_reg[2].sbr_slot, sa->sa_reg[2].sbr_offset); 191 sc->sc_fb_psize = (bus_size_t)sa->sa_reg[2].sbr_size; 192 193 fb->fb_driver = &p9100fbdriver; 194 fb->fb_device = &sc->sc_dev; 195 fb->fb_flags = sc->sc_dev.dv_cfdata->cf_flags & FB_USERMASK; 196 fb->fb_type.fb_type = FBTYPE_SUN3COLOR; 197 fb->fb_pixels = NULL; 198 199 node = sa->sa_node; 200 isconsole = fb_is_console(node); 201 if (!isconsole) { 202 printf("\n%s: fatal error: PROM didn't configure device: not console\n", self->dv_xname); 203 return; 204 } 205 206 /* 207 * When the ROM has mapped in a p9100 display, the address 208 * maps only the video RAM, so in any case we have to map the 209 * registers ourselves. We only need the video RAM if we are 210 * going to print characters via rconsole. 211 */ 212 if (sbus_bus_map(sc->sc_bustag, sc->sc_ctl_slot, 213 sa->sa_reg[0].sbr_slot + sa->sa_reg[0].sbr_offset, 214 sc->sc_ctl_psize, BUS_SPACE_MAP_LINEAR, 0, 215 &sc->sc_ctl_memh) != 0) { 216 printf("%s: cannot map control registers\n", self->dv_xname); 217 return; 218 } 219 220 if (sbus_bus_map(sc->sc_bustag, sc->sc_cmd_slot, 221 sa->sa_reg[1].sbr_slot + sa->sa_reg[1].sbr_offset, 222 sc->sc_cmd_psize, BUS_SPACE_MAP_LINEAR, 0, 223 &sc->sc_cmd_memh) != 0) { 224 printf("%s: cannot map command registers\n", self->dv_xname); 225 return; 226 } 227 228 if (sa->sa_npromvaddrs != 0) 229 fb->fb_pixels = (caddr_t)sa->sa_promvaddrs[0]; 230 231 if (fb->fb_pixels == NULL) { 232 if (sbus_bus_map(sc->sc_bustag, sc->sc_fb_slot, 233 sa->sa_reg[2].sbr_slot + 234 sa->sa_reg[2].sbr_offset, 235 sc->sc_fb_psize, 236 BUS_SPACE_MAP_LINEAR, 0, 237 &sc->sc_fb_memh) != 0) { 238 printf("%s: cannot map framebuffer\n", self->dv_xname); 239 return; 240 } 241 fb->fb_pixels = (char *)sc->sc_fb_memh; 242 } else { 243 sc->sc_fb_memh = (bus_space_handle_t) fb->fb_pixels; 244 } 245 246 i = p9100_ctl_read_4(sc, 0x0004); 247 switch ((i >> 26) & 7) { 248 case 5: fb->fb_type.fb_depth = 32; break; 249 case 7: fb->fb_type.fb_depth = 24; break; 250 case 3: fb->fb_type.fb_depth = 16; break; 251 case 2: fb->fb_type.fb_depth = 8; break; 252 default: { 253 panic("pnozz: can't determine screen depth (0x%02x)", i); 254 } 255 } 256 fb_setsize_obp(fb, fb->fb_type.fb_depth, 800, 600, node); 257 258 sbus_establish(&sc->sc_sd, &sc->sc_dev); 259 260 fb->fb_type.fb_size = fb->fb_type.fb_height * fb->fb_linebytes; 261 printf(": rev %d, %dx%d, depth %d", 262 (i & 7), fb->fb_type.fb_width, fb->fb_type.fb_height, 263 fb->fb_type.fb_depth); 264 265 fb->fb_type.fb_cmsize = PROM_getpropint(node, "cmsize", 256); 266 if ((1 << fb->fb_type.fb_depth) != fb->fb_type.fb_cmsize) 267 printf(", %d entry colormap", fb->fb_type.fb_cmsize); 268 269 /* Initialize the default color map. */ 270 bt_initcmap(&sc->sc_cmap, 256); 271 p9100loadcmap(sc, 0, 256); 272 273 /* make sure we are not blanked */ 274 if (isconsole) 275 p9100_set_video(sc, 1); 276 277 if (shutdownhook_establish(p9100_shutdown, sc) == NULL) { 278 panic("%s: could not establish shutdown hook", 279 sc->sc_dev.dv_xname); 280 } 281 282 if (isconsole) { 283 printf(" (console)\n"); 284 #ifdef RASTERCONSOLE 285 for (i = 0; i < fb->fb_type.fb_size; i++) { 286 if (fb->fb_pixels[i] == 0) { 287 fb->fb_pixels[i] = 1; 288 } else if (fb->fb_pixels[i] == (char) 255) { 289 fb->fb_pixels[i] = 0; 290 } 291 } 292 p9100loadcmap(sc, 255, 1); 293 fbrcons_init(fb); 294 #endif 295 } else 296 printf("\n"); 297 298 fb_attach(fb, isconsole); 299 } 300 301 static void 302 p9100_shutdown(arg) 303 void *arg; 304 { 305 struct p9100_softc *sc = arg; 306 #ifdef RASTERCONSOLE 307 struct fbdevice *fb = &sc->sc_fb; 308 int i; 309 310 for (i = 0; i < fb->fb_type.fb_size; i++) { 311 if (fb->fb_pixels[i] == 1) { 312 fb->fb_pixels[i] = 0; 313 } else if (fb->fb_pixels[i] == 0) { 314 fb->fb_pixels[i] = 255; 315 } 316 } 317 sc->sc_cmap.cm_map[0][0] = 0xff; 318 sc->sc_cmap.cm_map[0][1] = 0xff; 319 sc->sc_cmap.cm_map[0][2] = 0xff; 320 sc->sc_cmap.cm_map[1][0] = 0; 321 sc->sc_cmap.cm_map[1][1] = 0; 322 sc->sc_cmap.cm_map[1][2] = 0x80; 323 p9100loadcmap(sc, 0, 2); 324 sc->sc_cmap.cm_map[255][0] = 0; 325 sc->sc_cmap.cm_map[255][1] = 0; 326 sc->sc_cmap.cm_map[255][2] = 0; 327 p9100loadcmap(sc, 255, 1); 328 #endif 329 p9100_set_video(sc, 1); 330 } 331 332 int 333 p9100open(dev_t dev, int flags, int mode, struct proc *p) 334 { 335 int unit = minor(dev); 336 337 if (unit >= pnozz_cd.cd_ndevs || pnozz_cd.cd_devs[unit] == NULL) 338 return (ENXIO); 339 return (0); 340 } 341 342 int 343 p9100close(dev_t dev, int flags, int mode, struct proc *p) 344 { 345 return (0); 346 } 347 348 int 349 p9100ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) 350 { 351 struct p9100_softc *sc = pnozz_cd.cd_devs[minor(dev)]; 352 struct fbgattr *fba; 353 int error; 354 355 switch (cmd) { 356 357 case FBIOGTYPE: 358 *(struct fbtype *)data = sc->sc_fb.fb_type; 359 break; 360 361 case FBIOGATTR: 362 fba = (struct fbgattr *)data; 363 fba->real_type = sc->sc_fb.fb_type.fb_type; 364 fba->owner = 0; /* XXX ??? */ 365 fba->fbtype = sc->sc_fb.fb_type; 366 fba->sattr.flags = 0; 367 fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type; 368 fba->sattr.dev_specific[0] = -1; 369 fba->emu_types[0] = sc->sc_fb.fb_type.fb_type; 370 fba->emu_types[1] = -1; 371 break; 372 373 case FBIOGETCMAP: 374 #define p ((struct fbcmap *)data) 375 return (bt_getcmap(p, &sc->sc_cmap, 256, 1)); 376 377 case FBIOPUTCMAP: 378 /* copy to software map */ 379 error = bt_putcmap(p, &sc->sc_cmap, 256, 1); 380 if (error) 381 return (error); 382 /* now blast them into the chip */ 383 /* XXX should use retrace interrupt */ 384 p9100loadcmap(sc, p->index, p->count); 385 #undef p 386 break; 387 388 case FBIOGVIDEO: 389 *(int *)data = p9100_get_video(sc); 390 break; 391 392 case FBIOSVIDEO: 393 p9100_set_video(sc, *(int *)data); 394 break; 395 396 default: 397 return (ENOTTY); 398 } 399 return (0); 400 } 401 402 int 403 p9100poll(dev_t dev, int events, struct proc *p) 404 { 405 return seltrue(dev, events, p); 406 } 407 408 static uint32_t 409 p9100_ctl_read_4(struct p9100_softc *sc, bus_size_t off) 410 { 411 sc->sc_junk = bus_space_read_4(sc->sc_bustag, sc->sc_fb_memh, off); 412 return bus_space_read_4(sc->sc_bustag, sc->sc_ctl_memh, off); 413 } 414 415 static void 416 p9100_ctl_write_4(struct p9100_softc *sc, bus_size_t off, uint32_t v) 417 { 418 sc->sc_junk = bus_space_read_4(sc->sc_bustag, sc->sc_fb_memh, off); 419 bus_space_write_4(sc->sc_bustag, sc->sc_ctl_memh, off, v); 420 } 421 422 #if 0 423 static uint8_t 424 p9100_ramdac_read(struct p9100_softc *sc, bus_size_t off) 425 { 426 sc->sc_junk = p9100_ctl_read_4(sc, PWRUP_CNFG); 427 return p9100_ctl_read_4(sc, off) >> 16; 428 } 429 #endif 430 431 static void 432 p9100_ramdac_write(struct p9100_softc *sc, bus_size_t off, uint8_t v) 433 { 434 sc->sc_junk = p9100_ctl_read_4(sc, PWRUP_CNFG); 435 p9100_ctl_write_4(sc, off, v << 16); 436 } 437 438 /* 439 * Undo the effect of an FBIOSVIDEO that turns the video off. 440 */ 441 static void 442 p9100unblank(struct device *dev) 443 { 444 445 p9100_set_video((struct p9100_softc *)dev, 1); 446 } 447 448 static void 449 p9100_set_video(struct p9100_softc *sc, int enable) 450 { 451 u_int32_t v = p9100_ctl_read_4(sc, SCRN_RPNT_CTL_1); 452 if (enable) 453 v |= VIDEO_ENABLED; 454 else 455 v &= ~VIDEO_ENABLED; 456 p9100_ctl_write_4(sc, SCRN_RPNT_CTL_1, v); 457 #if NTCTRL > 0 458 /* Turn On/Off the TFT if we know how. 459 */ 460 tadpole_set_video(enable); 461 #endif 462 } 463 464 static int 465 p9100_get_video(struct p9100_softc *sc) 466 { 467 return (p9100_ctl_read_4(sc, SCRN_RPNT_CTL_1) & VIDEO_ENABLED) != 0; 468 } 469 470 /* 471 * Load a subset of the current (new) colormap into the IBM RAMDAC. 472 */ 473 static void 474 p9100loadcmap(struct p9100_softc *sc, int start, int ncolors) 475 { 476 u_char *p; 477 478 p9100_ramdac_write(sc, DAC_CMAP_WRIDX, start); 479 480 for (p = sc->sc_cmap.cm_map[start], ncolors *= 3; ncolors-- > 0; p++) { 481 p9100_ramdac_write(sc, DAC_CMAP_DATA, *p); 482 } 483 } 484 485 /* 486 * Return the address that would map the given device at the given 487 * offset, allowing for the given protection, or return -1 for error. 488 */ 489 paddr_t 490 p9100mmap(dev_t dev, off_t off, int prot) 491 { 492 struct p9100_softc *sc = pnozz_cd.cd_devs[minor(dev)]; 493 494 if (off & PGOFSET) 495 panic("p9100mmap"); 496 if (off < 0) 497 return (-1); 498 499 #define CG3_MMAP_OFFSET 0x04000000 500 /* Make Xsun think we are a CG3 (SUN3COLOR) 501 */ 502 if (off >= CG3_MMAP_OFFSET && off < CG3_MMAP_OFFSET + sc->sc_fb_psize) { 503 off -= CG3_MMAP_OFFSET; 504 return (bus_space_mmap(sc->sc_bustag, 505 sc->sc_fb_paddr, 506 off, 507 prot, 508 BUS_SPACE_MAP_LINEAR)); 509 } 510 511 if (off >= sc->sc_fb_psize + sc->sc_ctl_psize + sc->sc_cmd_psize) 512 return (-1); 513 514 if (off < sc->sc_fb_psize) { 515 return (bus_space_mmap(sc->sc_bustag, 516 sc->sc_fb_paddr, 517 off, 518 prot, 519 BUS_SPACE_MAP_LINEAR)); 520 } 521 off -= sc->sc_fb_psize; 522 if (off < sc->sc_ctl_psize) { 523 return (bus_space_mmap(sc->sc_bustag, 524 sc->sc_ctl_paddr, 525 off, 526 prot, 527 BUS_SPACE_MAP_LINEAR)); 528 } 529 off -= sc->sc_ctl_psize; 530 531 return (bus_space_mmap(sc->sc_bustag, 532 sc->sc_cmd_paddr, 533 off, 534 prot, 535 BUS_SPACE_MAP_LINEAR)); 536 } 537