1 /* $OpenBSD: vigra.c,v 1.14 2022/07/15 17:57:27 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2002, 2003, 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 Vigra VS series of SBus framebuffers. 32 * 33 * The VS10, VS11 and VS12 models are supported. VS10-EK is handled by the 34 * regular cgthree driver. 35 * 36 * The monochrome VS14, 16 grays VS15, and color VS18 are not supported. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/buf.h> 42 #include <sys/device.h> 43 #include <sys/ioctl.h> 44 #include <sys/malloc.h> 45 #include <sys/mman.h> 46 #include <sys/tty.h> 47 #include <sys/conf.h> 48 49 #include <uvm/uvm_extern.h> 50 51 #include <machine/autoconf.h> 52 #include <machine/bus.h> 53 #include <machine/pmap.h> 54 #include <machine/cpu.h> 55 #include <machine/conf.h> 56 57 #include <dev/wscons/wsconsio.h> 58 #include <dev/wscons/wsdisplayvar.h> 59 #include <dev/rasops/rasops.h> 60 #include <machine/fbvar.h> 61 62 #include <dev/sbus/sbusvar.h> 63 64 /* 65 * The hardware information below has been gathered through experiments, as 66 * well as the debug information of the SunOS 4.x vigfb driver. 67 */ 68 69 /* 70 * Control and status registers 71 */ 72 73 struct csregs { 74 u_int32_t sosr; 75 u_int32_t g3rr; 76 u_int32_t bcr; /* board control register */ 77 u_int32_t spr; 78 u_int32_t g3sr; /* ramdac status register */ 79 #define STATUS_INTR 0x0001 80 u_int32_t imr; /* interrupt mode register */ 81 u_int32_t ewcr; 82 u_int32_t ssr; 83 }; 84 85 /* 86 * G300 layout 87 */ 88 89 struct g300dac { 90 u_int32_t cmap[256]; 91 u_int32_t g3null; 92 u_int32_t unused1[32]; 93 u_int32_t half_sync; 94 u_int32_t back_porch; 95 u_int32_t display; 96 u_int32_t short_display; 97 u_int32_t broad_pulse; 98 u_int32_t vsync; 99 u_int32_t vblank; 100 u_int32_t vdisplay; 101 u_int32_t line_time; 102 u_int32_t tos1; 103 u_int32_t mem_init; 104 u_int32_t transfer_delay; 105 u_int32_t unused2[19]; 106 u_int32_t mask; 107 u_int32_t unused3[31]; 108 u_int32_t cr; 109 u_int32_t unused4[31]; 110 u_int32_t tos2; 111 u_int32_t unused5[31]; 112 u_int32_t boot_location; 113 }; 114 115 /* 116 * G335 layout 117 */ 118 119 struct g335dac { 120 u_int32_t boot_location; 121 u_int32_t unused1[32]; 122 u_int32_t half_sync; 123 u_int32_t back_porch; 124 u_int32_t display; 125 u_int32_t short_display; 126 u_int32_t broad_pulse; 127 u_int32_t vsync; 128 u_int32_t vpre_equalize; 129 u_int32_t vpost_equalize; 130 u_int32_t vblank; 131 u_int32_t vdisplay; 132 u_int32_t line_time; 133 u_int32_t tos1; 134 u_int32_t mem_init; 135 u_int32_t transfer_delay; 136 u_int32_t unused2[17]; 137 u_int32_t mask; 138 u_int32_t unused3[31]; 139 u_int32_t cra; 140 u_int32_t unused4[15]; 141 u_int32_t crb; 142 u_int32_t unused5[15]; 143 u_int32_t tos2; 144 u_int32_t unused6[32]; 145 u_int32_t cursor_palette[3]; 146 u_int32_t unused7[28]; 147 u_int32_t checksum[3]; 148 u_int32_t unused8[4]; 149 u_int32_t cursor_position; 150 u_int32_t unused9[56]; 151 u_int32_t cmap[256]; 152 u_int32_t cursor_store[512]; 153 }; 154 155 union dac { 156 struct g300dac g300; 157 struct g335dac g335; 158 }; 159 160 /* 161 * SBUS register mappings 162 */ 163 #define VIGRA_REG_RAMDAC 1 /* either G300 or G335 */ 164 #define VIGRA_REG_CSR 2 165 #define VIGRA_REG_VRAM 3 166 167 #define VIGRA_NREG 4 168 169 union vigracmap { 170 u_char cm_map[256][4]; /* 256 R/G/B entries plus pad */ 171 u_int32_t cm_chip[256]; /* the way the chip gets loaded */ 172 }; 173 174 /* per-display variables */ 175 struct vigra_softc { 176 struct sunfb sc_sunfb; /* common base part */ 177 bus_space_tag_t sc_bustag; 178 bus_addr_t sc_paddr; 179 volatile struct csregs *sc_regs;/* control registers */ 180 volatile union dac *sc_ramdac; /* ramdac registers */ 181 union vigracmap sc_cmap; /* current colormap */ 182 int sc_g300; 183 void *sc_ih; 184 int sc_nscreens; 185 }; 186 187 int vigra_ioctl(void *, u_long, caddr_t, int, struct proc *); 188 paddr_t vigra_mmap(void *, off_t, int); 189 void vigra_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t); 190 int vigra_getcmap(union vigracmap *, struct wsdisplay_cmap *, int); 191 int vigra_putcmap(union vigracmap *, struct wsdisplay_cmap *, int); 192 void vigra_loadcmap_immediate(struct vigra_softc *, int, int); 193 static __inline__ void vigra_loadcmap_deferred(struct vigra_softc *, 194 u_int, u_int); 195 void vigra_burner(void *, u_int, u_int); 196 int vigra_intr(void *); 197 198 struct wsdisplay_accessops vigra_accessops = { 199 .ioctl = vigra_ioctl, 200 .mmap = vigra_mmap, 201 .burn_screen = vigra_burner 202 }; 203 204 int vigramatch(struct device *, void *, void *); 205 void vigraattach(struct device *, struct device *, void *); 206 207 const struct cfattach vigra_ca = { 208 sizeof (struct vigra_softc), vigramatch, vigraattach 209 }; 210 211 struct cfdriver vigra_cd = { 212 NULL, "vigra", DV_DULL 213 }; 214 215 /* 216 * Match a supported vigra card. 217 */ 218 int 219 vigramatch(struct device *parent, void *vcf, void *aux) 220 { 221 struct sbus_attach_args *sa = aux; 222 223 if (strcmp("vs10", sa->sa_name) != 0 && 224 strcmp("vs11", sa->sa_name) != 0 && 225 strcmp("vs12", sa->sa_name) != 0) 226 return (0); 227 228 return (1); 229 } 230 231 /* 232 * Attach and initialize a vigra display, as well as a child wsdisplay. 233 */ 234 void 235 vigraattach(struct device *parent, struct device *self, void *args) 236 { 237 struct vigra_softc *sc = (struct vigra_softc *)self; 238 struct sbus_attach_args *sa = args; 239 bus_space_tag_t bt; 240 bus_space_handle_t bh; 241 int node, isconsole = 0; 242 char *nam; 243 244 bt = sa->sa_bustag; 245 node = sa->sa_node; 246 nam = getpropstring(node, "model"); 247 if (*nam == '\0') 248 nam = (char *)sa->sa_name; 249 printf(": %s", nam); 250 251 isconsole = node == fbnode; 252 253 if (sa->sa_nreg < VIGRA_NREG) { 254 printf("\n%s: expected %d registers, got %d", 255 self->dv_xname, VIGRA_NREG, sa->sa_nreg); 256 return; 257 } 258 259 /* 260 * Check whether we are using an G300 or an G335 chip. 261 * The VS10 and VS12 use the G300, while the VS11 uses a G335. 262 */ 263 sc->sc_g300 = strncmp(nam, "VIGRA,vs11", strlen("VIGRA,vs11")); 264 265 sc->sc_bustag = bt; 266 if (sbus_bus_map(bt, sa->sa_reg[VIGRA_REG_CSR].sbr_slot, 267 sa->sa_reg[VIGRA_REG_CSR].sbr_offset, 268 sa->sa_reg[VIGRA_REG_CSR].sbr_size, BUS_SPACE_MAP_LINEAR, 0, 269 &bh) != 0) { 270 printf("\n%s: can't map control registers\n", self->dv_xname); 271 return; 272 } 273 sc->sc_regs = bus_space_vaddr(bt, bh); 274 if (sbus_bus_map(bt, sa->sa_reg[VIGRA_REG_RAMDAC].sbr_slot, 275 sa->sa_reg[VIGRA_REG_RAMDAC].sbr_offset, 276 sa->sa_reg[VIGRA_REG_RAMDAC].sbr_size, BUS_SPACE_MAP_LINEAR, 0, 277 &bh) != 0) { 278 printf("\n%s: can't map ramdac registers\n", self->dv_xname); 279 return; 280 } 281 sc->sc_ramdac = bus_space_vaddr(bt, bh); 282 283 /* enable video */ 284 vigra_burner(sc, 1, 0); 285 286 fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, 0); 287 if (sbus_bus_map(bt, sa->sa_reg[VIGRA_REG_VRAM].sbr_slot, 288 sa->sa_reg[VIGRA_REG_VRAM].sbr_offset, 289 round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0, 290 &bh) != 0) { 291 printf("\n%s: can't map video memory\n", self->dv_xname); 292 return; 293 } 294 sc->sc_sunfb.sf_ro.ri_bits = bus_space_vaddr(bt, bh); 295 sc->sc_sunfb.sf_ro.ri_hw = sc; 296 sc->sc_paddr = sbus_bus_addr(bt, sa->sa_reg[VIGRA_REG_VRAM].sbr_slot, 297 sa->sa_reg[VIGRA_REG_VRAM].sbr_offset); 298 299 printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height); 300 301 if ((sc->sc_ih = bus_intr_establish(sa->sa_bustag, sa->sa_pri, 302 IPL_TTY, 0, vigra_intr, sc, self->dv_xname)) == NULL) { 303 printf("%s: couldn't establish interrupt, pri %d\n", 304 self->dv_xname, INTLEV(sa->sa_pri)); 305 } 306 307 fbwscons_init(&sc->sc_sunfb, 0, isconsole); 308 fbwscons_setcolormap(&sc->sc_sunfb, vigra_setcolor); 309 310 if (isconsole) 311 fbwscons_console_init(&sc->sc_sunfb, -1); 312 313 fbwscons_attach(&sc->sc_sunfb, &vigra_accessops, isconsole); 314 } 315 316 int 317 vigra_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p) 318 { 319 struct vigra_softc *sc = v; 320 struct wsdisplay_cmap *cm; 321 struct wsdisplay_fbinfo *wdf; 322 int error; 323 324 switch (cmd) { 325 case WSDISPLAYIO_GTYPE: 326 *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN; 327 break; 328 case WSDISPLAYIO_GINFO: 329 wdf = (struct wsdisplay_fbinfo *)data; 330 wdf->height = sc->sc_sunfb.sf_height; 331 wdf->width = sc->sc_sunfb.sf_width; 332 wdf->depth = sc->sc_sunfb.sf_depth; 333 wdf->stride = sc->sc_sunfb.sf_linebytes; 334 wdf->offset = 0; 335 wdf->cmsize = 256; 336 break; 337 case WSDISPLAYIO_LINEBYTES: 338 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 339 break; 340 341 case WSDISPLAYIO_GETCMAP: 342 cm = (struct wsdisplay_cmap *)data; 343 error = vigra_getcmap(&sc->sc_cmap, cm, sc->sc_g300); 344 if (error) 345 return (error); 346 break; 347 case WSDISPLAYIO_PUTCMAP: 348 cm = (struct wsdisplay_cmap *)data; 349 error = vigra_putcmap(&sc->sc_cmap, cm, sc->sc_g300); 350 if (error) 351 return (error); 352 /* if we can handle interrupts, defer the update */ 353 if (sc->sc_ih != NULL) 354 vigra_loadcmap_deferred(sc, cm->index, cm->count); 355 else 356 vigra_loadcmap_immediate(sc, cm->index, cm->count); 357 break; 358 359 case WSDISPLAYIO_SVIDEO: 360 case WSDISPLAYIO_GVIDEO: 361 break; 362 363 case WSDISPLAYIO_GCURPOS: 364 case WSDISPLAYIO_SCURPOS: 365 case WSDISPLAYIO_GCURMAX: 366 case WSDISPLAYIO_GCURSOR: 367 case WSDISPLAYIO_SCURSOR: 368 default: 369 return (-1); /* not supported yet */ 370 } 371 372 return (0); 373 } 374 375 /* 376 * Return the address that would map the given device at the given 377 * offset, allowing for the given protection, or return -1 for error. 378 */ 379 paddr_t 380 vigra_mmap(void *v, off_t offset, int prot) 381 { 382 struct vigra_softc *sc = v; 383 384 if (offset & PGOFSET) 385 return (-1); 386 387 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) { 388 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr, 389 offset, prot, BUS_SPACE_MAP_LINEAR)); 390 } 391 392 return (-1); 393 } 394 395 void 396 vigra_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) 397 { 398 struct vigra_softc *sc = v; 399 400 if (sc->sc_g300) { 401 sc->sc_cmap.cm_map[index][3] = r; 402 sc->sc_cmap.cm_map[index][2] = g; 403 sc->sc_cmap.cm_map[index][1] = b; 404 } else { 405 sc->sc_cmap.cm_map[index][3] = b; 406 sc->sc_cmap.cm_map[index][2] = g; 407 sc->sc_cmap.cm_map[index][1] = r; 408 } 409 sc->sc_cmap.cm_map[index][0] = 0; /* no alpha channel */ 410 411 vigra_loadcmap_immediate(sc, index, 1); 412 } 413 414 int 415 vigra_getcmap(union vigracmap *cm, struct wsdisplay_cmap *rcm, int g300) 416 { 417 u_int index = rcm->index, count = rcm->count, i; 418 int error; 419 420 if (index >= 256 || count > 256 - index) 421 return (EINVAL); 422 423 if (g300) { 424 for (i = 0; i < count; i++) { 425 if ((error = copyout(&cm->cm_map[index + i][3], 426 &rcm->red[i], 1)) != 0) 427 return (error); 428 if ((error = copyout(&cm->cm_map[index + i][1], 429 &rcm->blue[i], 1)) != 0) 430 return (error); 431 } 432 } else { 433 for (i = 0; i < count; i++) { 434 if ((error = copyout(&cm->cm_map[index + i][1], 435 &rcm->red[i], 1)) != 0) 436 return (error); 437 if ((error = copyout(&cm->cm_map[index + i][3], 438 &rcm->blue[i], 1)) != 0) 439 return (error); 440 } 441 } 442 443 for (i = 0; i < count; i++) { 444 if ((error = copyout(&cm->cm_map[index + i][2], 445 &rcm->green[i], 1)) != 0) 446 return (error); 447 } 448 return (0); 449 } 450 451 int 452 vigra_putcmap(union vigracmap *cm, struct wsdisplay_cmap *rcm, int g300) 453 { 454 u_int index = rcm->index, count = rcm->count, i; 455 int error; 456 457 if (index >= 256 || count > 256 - index) 458 return (EINVAL); 459 460 if (g300) { 461 for (i = 0; i < count; i++) { 462 if ((error = copyin(&rcm->red[i], 463 &cm->cm_map[index + i][3], 1)) != 0) 464 return (error); 465 if ((error = copyin(&rcm->blue[i], 466 &cm->cm_map[index + i][1], 1)) != 0) 467 return (error); 468 } 469 } else { 470 for (i = 0; i < count; i++) { 471 if ((error = copyin(&rcm->red[i], 472 &cm->cm_map[index + i][1], 1)) != 0) 473 return (error); 474 if ((error = copyin(&rcm->blue[i], 475 &cm->cm_map[index + i][3], 1)) != 0) 476 return (error); 477 } 478 } 479 480 for (i = 0; i < count; i++) { 481 if ((error = copyin(&rcm->green[i], 482 &cm->cm_map[index + i][2], 1)) != 0) 483 return (error); 484 cm->cm_map[index + i][0] = 0; /* no alpha channel */ 485 } 486 return (0); 487 } 488 489 void 490 vigra_loadcmap_immediate(struct vigra_softc *sc, int start, int ncolors) 491 { 492 u_int32_t *colp = &sc->sc_cmap.cm_chip[start]; 493 volatile u_int32_t *lutp; 494 495 if (sc->sc_g300) 496 lutp = &(sc->sc_ramdac->g300.cmap[start]); 497 else 498 lutp = &(sc->sc_ramdac->g335.cmap[start]); 499 500 while (--ncolors >= 0) 501 *lutp++ = *colp++; 502 } 503 504 static __inline__ void 505 vigra_loadcmap_deferred(struct vigra_softc *sc, u_int start, u_int ncolors) 506 { 507 508 sc->sc_regs->imr = 1; 509 } 510 511 void 512 vigra_burner(void *v, u_int on, u_int flags) 513 { 514 struct vigra_softc *sc = v; 515 516 if (on) { 517 sc->sc_regs->bcr = 0; 518 } else { 519 sc->sc_regs->bcr = 1; 520 } 521 } 522 523 int 524 vigra_intr(void *v) 525 { 526 struct vigra_softc *sc = v; 527 528 if (sc->sc_regs->imr == 0 || 529 !ISSET(sc->sc_regs->g3sr, STATUS_INTR)) { 530 /* Not expecting an interrupt, it's not for us. */ 531 return (0); 532 } 533 534 /* Acknowledge the interrupt and disable it. */ 535 sc->sc_regs->imr = 0; 536 537 vigra_loadcmap_immediate(sc, 0, 256); 538 539 return (1); 540 } 541