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