1 /* $OpenBSD: rfx.c,v 1.14 2022/03/13 13:34:54 mpi 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->cmsize = 256; 325 break; 326 case WSDISPLAYIO_LINEBYTES: 327 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 328 break; 329 330 case WSDISPLAYIO_GETCMAP: 331 cm = (struct wsdisplay_cmap *)data; 332 error = rfx_getcmap(&sc->sc_cmap, cm); 333 if (error != 0) 334 return (error); 335 break; 336 case WSDISPLAYIO_PUTCMAP: 337 cm = (struct wsdisplay_cmap *)data; 338 error = rfx_putcmap(&sc->sc_cmap, cm); 339 if (error != 0) 340 return (error); 341 rfx_loadcmap(sc, cm->index, cm->count); 342 break; 343 344 case WSDISPLAYIO_SVIDEO: 345 case WSDISPLAYIO_GVIDEO: 346 break; 347 348 default: 349 return (-1); 350 } 351 352 return (0); 353 } 354 355 paddr_t 356 rfx_mmap(void *v, off_t offset, int prot) 357 { 358 struct rfx_softc *sc = v; 359 360 if (offset & PGOFSET) 361 return (-1); 362 363 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) { 364 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr, 365 RFX_VRAM_ADDR + offset, prot, BUS_SPACE_MAP_LINEAR)); 366 } 367 368 return (-1); 369 } 370 371 void 372 rfx_burner(void *v, u_int on, u_int flags) 373 { 374 struct rfx_softc *sc = v; 375 376 if (on) { 377 sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VIDEO_DISABLE; 378 sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VSYNC_ENABLE; 379 } else { 380 sc->sc_ctrl[RFX_VIDCTRL_REG] |= RFX_VIDEO_DISABLE; 381 if (flags & WSDISPLAY_BURN_VBLANK) 382 sc->sc_ctrl[RFX_VIDCTRL_REG] &= ~RFX_VSYNC_ENABLE; 383 } 384 } 385 386 /* 387 * Colormap helper functions 388 */ 389 390 void 391 rfx_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) 392 { 393 struct rfx_softc *sc = v; 394 395 sc->sc_cmap.red[index] = r; 396 sc->sc_cmap.green[index] = g; 397 sc->sc_cmap.blue[index] = b; 398 399 rfx_loadcmap(sc, index, 1); 400 } 401 402 int 403 rfx_getcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm) 404 { 405 u_int index = rcm->index, count = rcm->count; 406 int error; 407 408 if (index >= 256 || count > 256 - index) 409 return (EINVAL); 410 411 if ((error = copyout(cm->red + index, rcm->red, count)) != 0) 412 return (error); 413 if ((error = copyout(cm->green + index, rcm->green, count)) != 0) 414 return (error); 415 if ((error = copyout(cm->blue + index, rcm->blue, count)) != 0) 416 return (error); 417 418 return (0); 419 } 420 421 int 422 rfx_putcmap(struct rfx_cmap *cm, struct wsdisplay_cmap *rcm) 423 { 424 u_int index = rcm->index, count = rcm->count; 425 u_int8_t red[256], green[256], blue[256]; 426 int error; 427 428 if (index >= 256 || count > 256 - index) 429 return (EINVAL); 430 431 if ((error = copyin(rcm->red, red, count)) != 0) 432 return (error); 433 if ((error = copyin(rcm->green, green, count)) != 0) 434 return (error); 435 if ((error = copyin(rcm->blue, blue, count)) != 0) 436 return (error); 437 438 bcopy(red, cm->red + index, count); 439 bcopy(green, cm->green + index, count); 440 bcopy(blue, cm->blue + index, count); 441 442 return (0); 443 } 444 445 void 446 rfx_loadcmap(struct rfx_softc *sc, int start, int ncolors) 447 { 448 u_int8_t *r, *g, *b; 449 450 r = sc->sc_cmap.red + start; 451 g = sc->sc_cmap.green + start; 452 b = sc->sc_cmap.blue + start; 453 454 start += BT463_IREG_CPALETTE_RAM; 455 sc->sc_ramdac[BT463_REG_ADDR_LOW] = start & 0xff; 456 sc->sc_ramdac[BT463_REG_ADDR_HIGH] = (start >> 8) & 0xff; 457 458 while (ncolors-- != 0) { 459 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *r++; 460 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *g++; 461 sc->sc_ramdac[BT463_REG_CMAP_DATA] = *b++; 462 } 463 } 464 465 /* 466 * Initialization code parser 467 */ 468 469 int 470 rfx_initialize(struct rfx_softc *sc, struct sbus_attach_args *sa, 471 struct rfx_config *cf) 472 { 473 u_int32_t *data, offset, value; 474 size_t cnt; 475 bus_space_handle_t bh; 476 int error; 477 478 /* 479 * Map the initialization data 480 */ 481 if ((error = sbus_bus_map(sa->sa_bustag, sa->sa_slot, sa->sa_offset + 482 RFX_INIT_ADDR, RFX_INIT_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh)) != 0) { 483 printf("\n%s: couldn't map initialization data\n", 484 sc->sc_sunfb.sf_dev.dv_xname); 485 return error; 486 } 487 data = (u_int32_t *)bus_space_vaddr(sa->sa_bustag, bh); 488 489 /* 490 * Skip copyright notice 491 */ 492 data += RFX_INIT_OFFSET / sizeof(u_int32_t); 493 cnt = (RFX_INIT_SIZE - RFX_INIT_OFFSET) / sizeof(u_int32_t); 494 cnt >>= 1; 495 496 /* 497 * Parse and apply settings 498 */ 499 while (cnt != 0) { 500 offset = *data++; 501 value = *data++; 502 503 if (offset == (u_int32_t)-1 && value == (u_int32_t)-1) 504 break; 505 506 /* Old PROM are little-endian */ 507 if (cf->version <= 1) { 508 offset = letoh32(offset); 509 value = letoh32(offset); 510 } 511 512 if (offset & (1U << 31)) { 513 offset = (offset & ~(1U << 31)) - RFX_RAMDAC_ADDR; 514 if (offset < RFX_RAMDAC_SIZE) 515 sc->sc_ramdac[offset] = value >> 24; 516 } else { 517 offset -= RFX_CONTROL_ADDR; 518 if (offset < RFX_CONTROL_SIZE) 519 sc->sc_ctrl[offset >> 2] = value; 520 } 521 522 cnt--; 523 } 524 525 #ifdef DEBUG 526 if (cnt != 0) 527 printf("%s: incoherent initialization data!\n", 528 sc->sc_sunfb.sf_dev.dv_xname); 529 #endif 530 531 bus_space_unmap(sa->sa_bustag, bh, RFX_INIT_SIZE); 532 533 return 0; 534 } 535