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