1 /* $OpenBSD: rfx.c,v 1.15 2022/07/15 17:57:27 kettenis 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 .ioctl = rfx_ioctl, 137 .mmap = rfx_mmap, 138 .burn_screen = rfx_burner 139 }; 140 141 int rfxmatch(struct device *, void *, void *); 142 void rfxattach(struct device *, struct device *, void *); 143 144 const struct cfattach rfx_ca = { 145 sizeof (struct rfx_softc), rfxmatch, rfxattach 146 }; 147 148 struct cfdriver rfx_cd = { 149 NULL, "rfx", DV_DULL 150 }; 151 152 /* 153 * Match a supported RasterFlex card. 154 */ 155 int 156 rfxmatch(struct device *parent, void *vcf, void *aux) 157 { 158 struct sbus_attach_args *sa = aux; 159 const char *device = sa->sa_name; 160 161 /* skip vendor name (could be CWARE, VITec, ...) */ 162 while (*device != ',' && *device != '\0') 163 device++; 164 if (*device == '\0') 165 device = sa->sa_name; 166 else 167 device++; 168 169 if (strncmp(device, "RasterFLEX", strlen("RasterFLEX")) != 0) 170 return (0); 171 172 /* RasterVideo and RasterFlex-TV are frame grabbers */ 173 if (strcmp(device, "RasterFLEX-TV") == 0) 174 return (0); 175 176 return (1); 177 } 178 179 /* 180 * Attach and initialize a rfx display, as well as a child wsdisplay. 181 */ 182 void 183 rfxattach(struct device *parent, struct device *self, void *args) 184 { 185 struct rfx_softc *sc = (struct rfx_softc *)self; 186 struct sbus_attach_args *sa = args; 187 const char *device = sa->sa_name; 188 struct rfx_config cf; 189 bus_space_tag_t bt; 190 bus_space_handle_t bh; 191 int node, cflen, isconsole = 0; 192 193 /* skip vendor name (could be CWARE, VITec, ...) */ 194 while (*device != ',' && *device != '\0') 195 device++; 196 if (*device == '\0') 197 device = sa->sa_name; 198 else 199 device++; 200 201 printf(": %s", device); 202 203 if (sa->sa_nreg == 0) { 204 printf("\n%s: no SBus registers!\n", self->dv_xname); 205 return; 206 } 207 208 bt = sa->sa_bustag; 209 node = sa->sa_node; 210 isconsole = node == fbnode; 211 212 /* 213 * Parse configuration structure 214 */ 215 cflen = getproplen(node, "configuration"); 216 if (cflen != sizeof cf) { 217 printf(", unknown %d bytes conf. structure", cflen); 218 /* fill in default values */ 219 cf.version = 0; 220 cf.scanline = 2048; 221 cf.width = 1152; 222 cf.height = 900; 223 } else { 224 OF_getprop(node, "configuration", &cf, cflen); 225 printf(", revision %d", cf.version); 226 } 227 228 /* 229 * Map registers 230 */ 231 232 sc->sc_bustag = bt; 233 sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset); 234 235 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_RAMDAC_ADDR, 236 RFX_RAMDAC_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) { 237 printf("\n%s: couldn't map ramdac registers\n", self->dv_xname); 238 return; 239 } 240 sc->sc_ramdac = (u_int8_t *)bus_space_vaddr(bt, bh); 241 242 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_CONTROL_ADDR, 243 RFX_CONTROL_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) { 244 printf("\n%s: couldn't map control registers\n", self->dv_xname); 245 return; 246 } 247 sc->sc_ctrl = (u_int32_t *)bus_space_vaddr(bt, bh); 248 249 #if 0 /* not yet */ 250 sc->sc_ih.ih_fun = rfx_intr; 251 sc->sc_ih.ih_arg = sc; 252 intr_establish(ca->ca_ra.ra_intr[0].int_pri, &sc->sc_ih, IPL_FB); 253 #endif 254 255 /* 256 * The following is an equivalent for 257 * fb_setsize(&sc->sc_sunfb, 8, cf.width, cf.height, 258 * node, ca->ca_bustype); 259 * forcing the correct scan line value. Since the usual frame buffer 260 * properties are missing on this card, no need to go through 261 * fb_setsize()... 262 */ 263 sc->sc_sunfb.sf_depth = 8; 264 sc->sc_sunfb.sf_width = cf.width; 265 sc->sc_sunfb.sf_height = cf.height; 266 sc->sc_sunfb.sf_linebytes = cf.scanline; 267 sc->sc_sunfb.sf_fbsize = cf.height * cf.scanline; 268 269 printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height); 270 271 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + RFX_VRAM_ADDR, 272 round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 273 0, &bh) != 0) { 274 printf("\n%s: couldn't map video memory\n", self->dv_xname); 275 return; 276 } 277 sc->sc_sunfb.sf_ro.ri_bits = bus_space_vaddr(bt, bh); 278 sc->sc_sunfb.sf_ro.ri_hw = sc; 279 280 /* 281 * If we are not the console, the frame buffer has not been 282 * initialized by the PROM - do this ourselves. 283 */ 284 if (!isconsole) { 285 if (rfx_initialize(sc, sa, &cf) != 0) 286 return; 287 } 288 289 fbwscons_init(&sc->sc_sunfb, 0, isconsole); 290 291 bzero(&sc->sc_cmap, sizeof(sc->sc_cmap)); 292 fbwscons_setcolormap(&sc->sc_sunfb, rfx_setcolor); 293 294 if (isconsole) 295 fbwscons_console_init(&sc->sc_sunfb, -1); 296 297 /* enable video */ 298 rfx_burner(sc, 1, 0); 299 300 fbwscons_attach(&sc->sc_sunfb, &rfx_accessops, isconsole); 301 } 302 303 /* 304 * Common wsdisplay operations 305 */ 306 307 int 308 rfx_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p) 309 { 310 struct rfx_softc *sc = v; 311 struct wsdisplay_cmap *cm; 312 struct wsdisplay_fbinfo *wdf; 313 int error; 314 315 switch (cmd) { 316 case WSDISPLAYIO_GTYPE: 317 *(u_int *)data = WSDISPLAY_TYPE_RFLEX; 318 break; 319 case WSDISPLAYIO_GINFO: 320 wdf = (struct wsdisplay_fbinfo *)data; 321 wdf->height = sc->sc_sunfb.sf_height; 322 wdf->width = sc->sc_sunfb.sf_width; 323 wdf->depth = sc->sc_sunfb.sf_depth; 324 wdf->stride = sc->sc_sunfb.sf_linebytes; 325 wdf->offset = 0; 326 wdf->cmsize = 256; 327 break; 328 case WSDISPLAYIO_LINEBYTES: 329 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 330 break; 331 332 case WSDISPLAYIO_GETCMAP: 333 cm = (struct wsdisplay_cmap *)data; 334 error = rfx_getcmap(&sc->sc_cmap, cm); 335 if (error != 0) 336 return (error); 337 break; 338 case WSDISPLAYIO_PUTCMAP: 339 cm = (struct wsdisplay_cmap *)data; 340 error = rfx_putcmap(&sc->sc_cmap, cm); 341 if (error != 0) 342 return (error); 343 rfx_loadcmap(sc, cm->index, cm->count); 344 break; 345 346 case WSDISPLAYIO_SVIDEO: 347 case WSDISPLAYIO_GVIDEO: 348 break; 349 350 default: 351 return (-1); 352 } 353 354 return (0); 355 } 356 357 paddr_t 358 rfx_mmap(void *v, off_t offset, int prot) 359 { 360 struct rfx_softc *sc = v; 361 362 if (offset & PGOFSET) 363 return (-1); 364 365 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) { 366 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr, 367 RFX_VRAM_ADDR + offset, prot, BUS_SPACE_MAP_LINEAR)); 368 } 369 370 return (-1); 371 } 372 373 void 374 rfx_burner(void *v, u_int on, u_int flags) 375 { 376 struct rfx_softc *sc = v; 377 378 if (on) { 379 sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VIDEO_DISABLE; 380 sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VSYNC_ENABLE; 381 } else { 382 sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VIDEO_DISABLE; 383 if (flags & WSDISPLAY_BURN_VBLANK) 384 sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VSYNC_ENABLE; 385 } 386 } 387 388 /* 389 * Colormap helper functions 390 */ 391 392 void 393 rfx_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) 394 { 395 struct rfx_softc *sc = v; 396 397 sc->sc_cmap.red[index] = r; 398 sc->sc_cmap.green[index] = g; 399 sc->sc_cmap.blue[index] = b; 400 401 rfx_loadcmap(sc, index, 1); 402 } 403 404 int 405 rfx_getcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm) 406 { 407 u_int index = rcm->index, count = rcm->count; 408 int error; 409 410 if (index >= 256 || count > 256 - index) 411 return (EINVAL); 412 413 if ((error = copyout(cm->red + index, rcm->red, count)) != 0) 414 return (error); 415 if ((error = copyout(cm->green + index, rcm->green, count)) != 0) 416 return (error); 417 if ((error = copyout(cm->blue + index, rcm->blue, count)) != 0) 418 return (error); 419 420 return (0); 421 } 422 423 int 424 rfx_putcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm) 425 { 426 u_int index = rcm->index, count = rcm->count; 427 u_int8_t red[256], green[256], blue[256]; 428 int error; 429 430 if (index >= 256 || count > 256 - index) 431 return (EINVAL); 432 433 if ((error = copyin(rcm->red, red, count)) != 0) 434 return (error); 435 if ((error = copyin(rcm->green, green, count)) != 0) 436 return (error); 437 if ((error = copyin(rcm->blue, blue, count)) != 0) 438 return (error); 439 440 bcopy(red, cm->red + index, count); 441 bcopy(green, cm->green + index, count); 442 bcopy(blue, cm->blue + index, count); 443 444 return (0); 445 } 446 447 void 448 rfx_loadcmap(struct rfx_softc *sc, int start, int ncolors) 449 { 450 u_int8_t *r, *g, *b; 451 452 r = sc->sc_cmap.red + start; 453 g = sc->sc_cmap.green + start; 454 b = sc->sc_cmap.blue + start; 455 456 start += BT463_IREG_CPALETTE_RAM; 457 sc->sc_ramdac[BT463_REG_ADDR_LOW] = start & 0xff; 458 sc->sc_ramdac[BT463_REG_ADDR_HIGH] = (start >> 8) & 0xff; 459 460 while (ncolors-- != 0) { 461 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *r++; 462 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *g++; 463 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *b++; 464 } 465 } 466 467 /* 468 * Initialization code parser 469 */ 470 471 int 472 rfx_initialize(struct rfx_softc *sc, struct sbus_attach_args *sa, 473 struct rfx_config *cf) 474 { 475 u_int32_t *data, offset, value; 476 size_t cnt; 477 bus_space_handle_t bh; 478 int error; 479 480 /* 481 * Map the initialization data 482 */ 483 if ((error = sbus_bus_map(sa->sa_bustag, sa->sa_slot, sa->sa_offset + 484 RFX_INIT_ADDR, RFX_INIT_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh)) != 0) { 485 printf("\n%s: couldn't map initialization data\n", 486 sc->sc_sunfb.sf_dev.dv_xname); 487 return error; 488 } 489 data = (u_int32_t *)bus_space_vaddr(sa->sa_bustag, bh); 490 491 /* 492 * Skip copyright notice 493 */ 494 data += RFX_INIT_OFFSET / sizeof(u_int32_t); 495 cnt = (RFX_INIT_SIZE - RFX_INIT_OFFSET) / sizeof(u_int32_t); 496 cnt >>= 1; 497 498 /* 499 * Parse and apply settings 500 */ 501 while (cnt != 0) { 502 offset = *data++; 503 value = *data++; 504 505 if (offset == (u_int32_t)-1 && value == (u_int32_t)-1) 506 break; 507 508 /* Old PROM are little-endian */ 509 if (cf->version <= 1) { 510 offset = letoh32(offset); 511 value = letoh32(offset); 512 } 513 514 if (offset & (1U << 31)) { 515 offset = (offset & ~(1U << 31)) - RFX_RAMDAC_ADDR; 516 if (offset < RFX_RAMDAC_SIZE) 517 sc->sc_ramdac[offset] = value >> 24; 518 } else { 519 offset -= RFX_CONTROL_ADDR; 520 if (offset < RFX_CONTROL_SIZE) 521 sc->sc_ctrl[offset >> 2] = value; 522 } 523 524 cnt--; 525 } 526 527 #ifdef DEBUG 528 if (cnt != 0) 529 printf("%s: incoherent initialization data!\n", 530 sc->sc_sunfb.sf_dev.dv_xname); 531 #endif 532 533 bus_space_unmap(sa->sa_bustag, bh, RFX_INIT_SIZE); 534 535 return 0; 536 } 537