1 /* $OpenBSD: rfx.c,v 1.9 2008/12/27 17:23:03 miod Exp $ */ 2 3 /* 4 * Copyright (c) 2004, Miodrag Vallat. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 * 28 */ 29 30 /* 31 * Driver for the Vitec RasterFlex family of frame buffers. 32 * It should support RasterFlex-24, RasterFlex-32 and RasterFlex-HR. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/buf.h> 38 #include <sys/device.h> 39 #include <sys/ioctl.h> 40 #include <sys/malloc.h> 41 #include <sys/mman.h> 42 #include <sys/tty.h> 43 #include <sys/conf.h> 44 45 #include <uvm/uvm_extern.h> 46 47 #include <machine/autoconf.h> 48 #include <machine/pmap.h> 49 #include <machine/cpu.h> 50 #include <machine/conf.h> 51 #include <machine/openfirm.h> 52 53 #include <dev/wscons/wsconsio.h> 54 #include <dev/wscons/wsdisplayvar.h> 55 #include <dev/rasops/rasops.h> 56 #include <machine/fbvar.h> 57 58 #include <dev/sbus/sbusvar.h> 59 60 #include <dev/ic/bt463reg.h> 61 62 /* 63 * Configuration structure 64 */ 65 struct rfx_config { 66 u_int16_t unknown; 67 u_int16_t version; 68 u_int32_t scanline; 69 u_int32_t maxwidth; /* unsure */ 70 u_int32_t maxheight; /* unsure */ 71 u_int32_t width; 72 u_int32_t height; 73 }; 74 75 /* 76 * In-memory offsets 77 */ 78 79 #define RFX_RAMDAC_ADDR 0x00020000 80 #define RFX_RAMDAC_SIZE 0x00000004 81 82 #define RFX_CONTROL_ADDR 0x00040000 83 #define RFX_CONTROL_SIZE 0x000000e0 84 85 #define RFX_INIT_ADDR 0x00018000 86 #define RFX_INIT_OFFSET 0x0000001c 87 #define RFX_INIT_SIZE 0x00008000 88 89 #define RFX_VRAM_ADDR 0x00100000 90 91 /* 92 * Control registers 93 */ 94 95 #define RFX_VIDCTRL_REG 0x10 96 #define RFX_VSYNC_ENABLE 0x00000001 97 #define RFX_VIDEO_DISABLE 0x00000002 98 99 /* 100 * Shadow colormap 101 */ 102 struct rfx_cmap { 103 u_int8_t red[256]; 104 u_int8_t green[256]; 105 u_int8_t blue[256]; 106 }; 107 108 struct rfx_softc { 109 struct sunfb sc_sunfb; 110 111 bus_space_tag_t sc_bustag; 112 bus_addr_t sc_paddr; 113 114 struct intrhand sc_ih; 115 116 struct rfx_cmap sc_cmap; 117 volatile u_int8_t *sc_ramdac; 118 volatile u_int32_t *sc_ctrl; 119 120 int sc_nscreens; 121 }; 122 123 void rfx_burner(void *, u_int, u_int); 124 int rfx_ioctl(void *, u_long, caddr_t, int, struct proc *); 125 paddr_t rfx_mmap(void *, off_t, int); 126 127 int rfx_getcmap(struct rfx_cmap *, struct wsdisplay_cmap *); 128 int rfx_initialize(struct rfx_softc *, struct sbus_attach_args *, 129 struct rfx_config *); 130 int rfx_intr(void *); 131 void rfx_loadcmap(struct rfx_softc *, int, int); 132 int rfx_putcmap(struct rfx_cmap *, struct wsdisplay_cmap *); 133 void rfx_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t); 134 135 struct wsdisplay_accessops rfx_accessops = { 136 rfx_ioctl, 137 rfx_mmap, 138 NULL, /* alloc_screen */ 139 NULL, /* free_screen */ 140 NULL, /* show_screen */ 141 NULL, /* load_font */ 142 NULL, /* scrollback */ 143 NULL, /* getchar */ 144 rfx_burner, 145 }; 146 147 int rfxmatch(struct device *, void *, void *); 148 void rfxattach(struct device *, struct device *, void *); 149 150 #if defined(OpenBSD) 151 struct cfattach rfx_ca = { 152 sizeof (struct rfx_softc), rfxmatch, rfxattach 153 }; 154 155 struct cfdriver rfx_cd = { 156 NULL, "rfx", DV_DULL 157 }; 158 #else 159 CFATTACH_DECL(rfx, sizeof (struct rfx_softc), rfxmatch, rfxattach, NULL, NULL); 160 #endif 161 162 /* 163 * Match a supported RasterFlex card. 164 */ 165 int 166 rfxmatch(struct device *parent, void *vcf, void *aux) 167 { 168 struct sbus_attach_args *sa = aux; 169 const char *device = sa->sa_name; 170 171 /* skip vendor name (could be CWARE, VITec, ...) */ 172 while (*device != ',' && *device != '\0') 173 device++; 174 if (*device == '\0') 175 device = sa->sa_name; 176 else 177 device++; 178 179 if (strncmp(device, "RasterFLEX", strlen("RasterFLEX")) != 0) 180 return (0); 181 182 /* RasterVideo and RasterFlex-TV are frame grabbers */ 183 if (strcmp(device, "RasterFLEX-TV") == 0) 184 return (0); 185 186 return (1); 187 } 188 189 /* 190 * Attach and initialize a rfx display, as well as a child wsdisplay. 191 */ 192 void 193 rfxattach(struct device *parent, struct device *self, void *args) 194 { 195 struct rfx_softc *sc = (struct rfx_softc *)self; 196 struct sbus_attach_args *sa = args; 197 const char *device = sa->sa_name; 198 struct rfx_config cf; 199 bus_space_tag_t bt; 200 bus_space_handle_t bh; 201 int node, cflen, isconsole = 0; 202 203 /* skip vendor name (could be CWARE, VITec, ...) */ 204 while (*device != ',' && *device != '\0') 205 device++; 206 if (*device == '\0') 207 device = sa->sa_name; 208 else 209 device++; 210 211 printf(": %s", device); 212 213 if (sa->sa_nreg == 0) { 214 printf("\n%s: no SBus registers!\n", self->dv_xname); 215 return; 216 } 217 218 bt = sa->sa_bustag; 219 node = sa->sa_node; 220 isconsole = node == fbnode; 221 222 /* 223 * Parse configuration structure 224 */ 225 cflen = getproplen(node, "configuration"); 226 if (cflen != sizeof cf) { 227 printf(", unknown %d bytes conf. structure", cflen); 228 /* fill in default values */ 229 cf.version = 0; 230 cf.scanline = 2048; 231 cf.width = 1152; 232 cf.height = 900; 233 } else { 234 OF_getprop(node, "configuration", &cf, cflen); 235 printf(", revision %d", cf.version); 236 } 237 238 /* 239 * Map registers 240 */ 241 242 sc->sc_bustag = bt; 243 sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset); 244 245 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_RAMDAC_ADDR, 246 RFX_RAMDAC_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) { 247 printf("\n%s: couldn't map ramdac registers\n", self->dv_xname); 248 return; 249 } 250 sc->sc_ramdac = (u_int8_t *)bus_space_vaddr(bt, bh); 251 252 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_CONTROL_ADDR, 253 RFX_CONTROL_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) { 254 printf("\n%s: couldn't map control registers\n", self->dv_xname); 255 return; 256 } 257 sc->sc_ctrl = (u_int32_t *)bus_space_vaddr(bt, bh); 258 259 #if 0 /* not yet */ 260 sc->sc_ih.ih_fun = rfx_intr; 261 sc->sc_ih.ih_arg = sc; 262 intr_establish(ca->ca_ra.ra_intr[0].int_pri, &sc->sc_ih, IPL_FB); 263 #endif 264 265 /* 266 * The following is an equivalent for 267 * fb_setsize(&sc->sc_sunfb, 8, cf.width, cf.height, 268 * node, ca->ca_bustype); 269 * forcing the correct scan line value. Since the usual frame buffer 270 * properties are missing on this card, no need to go through 271 * fb_setsize()... 272 */ 273 sc->sc_sunfb.sf_depth = 8; 274 sc->sc_sunfb.sf_width = cf.width; 275 sc->sc_sunfb.sf_height = cf.height; 276 sc->sc_sunfb.sf_linebytes = cf.scanline; 277 sc->sc_sunfb.sf_fbsize = cf.height * cf.scanline; 278 279 printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height); 280 281 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_VRAM_ADDR, 282 round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 283 0, &bh) != 0) { 284 printf("\n%s: couldn't map video memory\n", self->dv_xname); 285 return; 286 } 287 sc->sc_sunfb.sf_ro.ri_bits = bus_space_vaddr(bt, bh); 288 sc->sc_sunfb.sf_ro.ri_hw = sc; 289 290 /* 291 * If we are not the console, the frame buffer has not been 292 * initialized by the PROM - do this ourselves. 293 */ 294 if (!isconsole) { 295 if (rfx_initialize(sc, sa, &cf) != 0) 296 return; 297 } 298 299 fbwscons_init(&sc->sc_sunfb, 0, isconsole); 300 301 bzero(&sc->sc_cmap, sizeof(sc->sc_cmap)); 302 fbwscons_setcolormap(&sc->sc_sunfb, rfx_setcolor); 303 304 if (isconsole) 305 fbwscons_console_init(&sc->sc_sunfb, -1); 306 307 /* enable video */ 308 rfx_burner(sc, 1, 0); 309 310 fbwscons_attach(&sc->sc_sunfb, &rfx_accessops, isconsole); 311 } 312 313 /* 314 * Common wsdisplay operations 315 */ 316 317 int 318 rfx_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p) 319 { 320 struct rfx_softc *sc = v; 321 struct wsdisplay_cmap *cm; 322 struct wsdisplay_fbinfo *wdf; 323 int error; 324 325 switch (cmd) { 326 case WSDISPLAYIO_GTYPE: 327 *(u_int *)data = WSDISPLAY_TYPE_RFLEX; 328 break; 329 case WSDISPLAYIO_GINFO: 330 wdf = (struct wsdisplay_fbinfo *)data; 331 wdf->height = sc->sc_sunfb.sf_height; 332 wdf->width = sc->sc_sunfb.sf_width; 333 wdf->depth = sc->sc_sunfb.sf_depth; 334 wdf->cmsize = 256; 335 break; 336 case WSDISPLAYIO_LINEBYTES: 337 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 338 break; 339 340 case WSDISPLAYIO_GETCMAP: 341 cm = (struct wsdisplay_cmap *)data; 342 error = rfx_getcmap(&sc->sc_cmap, cm); 343 if (error != 0) 344 return (error); 345 break; 346 case WSDISPLAYIO_PUTCMAP: 347 cm = (struct wsdisplay_cmap *)data; 348 error = rfx_putcmap(&sc->sc_cmap, cm); 349 if (error != 0) 350 return (error); 351 rfx_loadcmap(sc, cm->index, cm->count); 352 break; 353 354 case WSDISPLAYIO_SVIDEO: 355 case WSDISPLAYIO_GVIDEO: 356 break; 357 358 default: 359 return (-1); 360 } 361 362 return (0); 363 } 364 365 paddr_t 366 rfx_mmap(void *v, off_t offset, int prot) 367 { 368 struct rfx_softc *sc = v; 369 370 if (offset & PGOFSET) 371 return (-1); 372 373 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) { 374 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr, 375 RFX_VRAM_ADDR + offset, prot, BUS_SPACE_MAP_LINEAR)); 376 } 377 378 return (-1); 379 } 380 381 void 382 rfx_burner(void *v, u_int on, u_int flags) 383 { 384 struct rfx_softc *sc = v; 385 386 if (on) { 387 sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VIDEO_DISABLE; 388 sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VSYNC_ENABLE; 389 } else { 390 sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VIDEO_DISABLE; 391 if (flags & WSDISPLAY_BURN_VBLANK) 392 sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VSYNC_ENABLE; 393 } 394 } 395 396 /* 397 * Colormap helper functions 398 */ 399 400 void 401 rfx_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) 402 { 403 struct rfx_softc *sc = v; 404 405 sc->sc_cmap.red[index] = r; 406 sc->sc_cmap.green[index] = g; 407 sc->sc_cmap.blue[index] = b; 408 409 rfx_loadcmap(sc, index, 1); 410 } 411 412 int 413 rfx_getcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm) 414 { 415 u_int index = rcm->index, count = rcm->count; 416 int error; 417 418 if (index >= 256 || count > 256 - index) 419 return (EINVAL); 420 421 if ((error = copyout(cm->red + index, rcm->red, count)) != 0) 422 return (error); 423 if ((error = copyout(cm->green + index, rcm->green, count)) != 0) 424 return (error); 425 if ((error = copyout(cm->blue + index, rcm->blue, count)) != 0) 426 return (error); 427 428 return (0); 429 } 430 431 int 432 rfx_putcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm) 433 { 434 u_int index = rcm->index, count = rcm->count; 435 u_int8_t red[256], green[256], blue[256]; 436 int error; 437 438 if (index >= 256 || count > 256 - index) 439 return (EINVAL); 440 441 if ((error = copyin(rcm->red, red, count)) != 0) 442 return (error); 443 if ((error = copyin(rcm->green, green, count)) != 0) 444 return (error); 445 if ((error = copyin(rcm->blue, blue, count)) != 0) 446 return (error); 447 448 bcopy(red, cm->red + index, count); 449 bcopy(green, cm->green + index, count); 450 bcopy(blue, cm->blue + index, count); 451 452 return (0); 453 } 454 455 void 456 rfx_loadcmap(struct rfx_softc *sc, int start, int ncolors) 457 { 458 u_int8_t *r, *g, *b; 459 460 r = sc->sc_cmap.red + start; 461 g = sc->sc_cmap.green + start; 462 b = sc->sc_cmap.blue + start; 463 464 start += BT463_IREG_CPALETTE_RAM; 465 sc->sc_ramdac[BT463_REG_ADDR_LOW] = start & 0xff; 466 sc->sc_ramdac[BT463_REG_ADDR_HIGH] = (start >> 8) & 0xff; 467 468 while (ncolors-- != 0) { 469 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *r++; 470 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *g++; 471 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *b++; 472 } 473 } 474 475 /* 476 * Initialization code parser 477 */ 478 479 int 480 rfx_initialize(struct rfx_softc *sc, struct sbus_attach_args *sa, 481 struct rfx_config *cf) 482 { 483 u_int32_t *data, offset, value; 484 size_t cnt; 485 bus_space_handle_t bh; 486 int error; 487 488 /* 489 * Map the initialization data 490 */ 491 if ((error = sbus_bus_map(sa->sa_bustag, sa->sa_slot, sa->sa_offset + 492 RFX_INIT_ADDR, RFX_INIT_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh)) != 0) { 493 printf("\n%s: couldn't map initialization data\n", 494 sc->sc_sunfb.sf_dev.dv_xname); 495 return error; 496 } 497 data = (u_int32_t *)bus_space_vaddr(sa->sa_bustag, bh); 498 499 /* 500 * Skip copyright notice 501 */ 502 data += RFX_INIT_OFFSET / sizeof(u_int32_t); 503 cnt = (RFX_INIT_SIZE - RFX_INIT_OFFSET) / sizeof(u_int32_t); 504 cnt >>= 1; 505 506 /* 507 * Parse and apply settings 508 */ 509 while (cnt != 0) { 510 offset = *data++; 511 value = *data++; 512 513 if (offset == (u_int32_t)-1 && value == (u_int32_t)-1) 514 break; 515 516 /* Old PROM are little-endian */ 517 if (cf->version <= 1) { 518 offset = letoh32(offset); 519 value = letoh32(offset); 520 } 521 522 if (offset & (1 << 31)) { 523 offset = (offset & ~(1 << 31)) - RFX_RAMDAC_ADDR; 524 if (offset < RFX_RAMDAC_SIZE) 525 sc->sc_ramdac[offset] = value >> 24; 526 } else { 527 offset -= RFX_CONTROL_ADDR; 528 if (offset < RFX_CONTROL_SIZE) 529 sc->sc_ctrl[offset >> 2] = value; 530 } 531 532 cnt--; 533 } 534 535 #ifdef DEBUG 536 if (cnt != 0) 537 printf("%s: incoherent initialization data!\n"); 538 #endif 539 540 bus_space_unmap(sa->sa_bustag, bh, RFX_INIT_SIZE); 541 542 return 0; 543 } 544