1 /* $OpenBSD: vigra.c,v 1.11 2008/12/27 17:23:03 miod 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 vigra_ioctl, 200 vigra_mmap, 201 NULL, /* alloc_screen */ 202 NULL, /* free_screen */ 203 NULL, /* show_screen */ 204 NULL, /* load_font */ 205 NULL, /* scrollback */ 206 NULL, /* getchar */ 207 vigra_burner, 208 }; 209 210 int vigramatch(struct device *, void *, void *); 211 void vigraattach(struct device *, struct device *, void *); 212 213 struct cfattach vigra_ca = { 214 sizeof (struct vigra_softc), vigramatch, vigraattach 215 }; 216 217 struct cfdriver vigra_cd = { 218 NULL, "vigra", DV_DULL 219 }; 220 221 /* 222 * Match a supported vigra card. 223 */ 224 int 225 vigramatch(struct device *parent, void *vcf, void *aux) 226 { 227 struct sbus_attach_args *sa = aux; 228 229 if (strcmp("vs10", sa->sa_name) != 0 && 230 strcmp("vs11", sa->sa_name) != 0 && 231 strcmp("vs12", sa->sa_name) != 0) 232 return (0); 233 234 return (1); 235 } 236 237 /* 238 * Attach and initialize a vigra display, as well as a child wsdisplay. 239 */ 240 void 241 vigraattach(struct device *parent, struct device *self, void *args) 242 { 243 struct vigra_softc *sc = (struct vigra_softc *)self; 244 struct sbus_attach_args *sa = args; 245 bus_space_tag_t bt; 246 bus_space_handle_t bh; 247 int node, isconsole = 0; 248 char *nam; 249 250 bt = sa->sa_bustag; 251 node = sa->sa_node; 252 nam = getpropstring(node, "model"); 253 if (*nam == '\0') 254 nam = (char *)sa->sa_name; 255 printf(": %s", nam); 256 257 isconsole = node == fbnode; 258 259 if (sa->sa_nreg < VIGRA_NREG) { 260 printf("\n%s: expected %d registers, got %d", 261 self->dv_xname, VIGRA_NREG, sa->sa_nreg); 262 return; 263 } 264 265 /* 266 * Check whether we are using an G300 or an G335 chip. 267 * The VS10 and VS12 use the G300, while the VS11 uses a G335. 268 */ 269 sc->sc_g300 = strncmp(nam, "VIGRA,vs11", strlen("VIGRA,vs11")); 270 271 sc->sc_bustag = bt; 272 if (sbus_bus_map(bt, sa->sa_reg[VIGRA_REG_CSR].sbr_slot, 273 sa->sa_reg[VIGRA_REG_CSR].sbr_offset, 274 sa->sa_reg[VIGRA_REG_CSR].sbr_size, BUS_SPACE_MAP_LINEAR, 0, 275 &bh) != 0) { 276 printf("\n%s: can't map control registers\n", self->dv_xname); 277 return; 278 } 279 sc->sc_regs = bus_space_vaddr(bt, bh); 280 if (sbus_bus_map(bt, sa->sa_reg[VIGRA_REG_RAMDAC].sbr_slot, 281 sa->sa_reg[VIGRA_REG_RAMDAC].sbr_offset, 282 sa->sa_reg[VIGRA_REG_RAMDAC].sbr_size, BUS_SPACE_MAP_LINEAR, 0, 283 &bh) != 0) { 284 printf("\n%s: can't map ramdac registers\n", self->dv_xname); 285 return; 286 } 287 sc->sc_ramdac = bus_space_vaddr(bt, bh); 288 289 /* enable video */ 290 vigra_burner(sc, 1, 0); 291 292 fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, 0); 293 if (sbus_bus_map(bt, sa->sa_reg[VIGRA_REG_VRAM].sbr_slot, 294 sa->sa_reg[VIGRA_REG_VRAM].sbr_offset, 295 round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0, 296 &bh) != 0) { 297 printf("\n%s: can't map video memory\n", self->dv_xname); 298 return; 299 } 300 sc->sc_sunfb.sf_ro.ri_bits = bus_space_vaddr(bt, bh); 301 sc->sc_sunfb.sf_ro.ri_hw = sc; 302 sc->sc_paddr = sbus_bus_addr(bt, sa->sa_reg[VIGRA_REG_VRAM].sbr_slot, 303 sa->sa_reg[VIGRA_REG_VRAM].sbr_offset); 304 305 printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height); 306 307 if ((sc->sc_ih = bus_intr_establish(sa->sa_bustag, sa->sa_pri, 308 IPL_TTY, 0, vigra_intr, sc, self->dv_xname)) == NULL) { 309 printf("%s: couldn't establish interrupt, pri %d\n", 310 self->dv_xname, INTLEV(sa->sa_pri)); 311 } 312 313 fbwscons_init(&sc->sc_sunfb, 0, isconsole); 314 fbwscons_setcolormap(&sc->sc_sunfb, vigra_setcolor); 315 316 if (isconsole) 317 fbwscons_console_init(&sc->sc_sunfb, -1); 318 319 fbwscons_attach(&sc->sc_sunfb, &vigra_accessops, isconsole); 320 } 321 322 int 323 vigra_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p) 324 { 325 struct vigra_softc *sc = v; 326 struct wsdisplay_cmap *cm; 327 struct wsdisplay_fbinfo *wdf; 328 int error; 329 330 switch (cmd) { 331 case WSDISPLAYIO_GTYPE: 332 *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN; 333 break; 334 case WSDISPLAYIO_GINFO: 335 wdf = (struct wsdisplay_fbinfo *)data; 336 wdf->height = sc->sc_sunfb.sf_height; 337 wdf->width = sc->sc_sunfb.sf_width; 338 wdf->depth = sc->sc_sunfb.sf_depth; 339 wdf->cmsize = 256; 340 break; 341 case WSDISPLAYIO_LINEBYTES: 342 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 343 break; 344 345 case WSDISPLAYIO_GETCMAP: 346 cm = (struct wsdisplay_cmap *)data; 347 error = vigra_getcmap(&sc->sc_cmap, cm, sc->sc_g300); 348 if (error) 349 return (error); 350 break; 351 case WSDISPLAYIO_PUTCMAP: 352 cm = (struct wsdisplay_cmap *)data; 353 error = vigra_putcmap(&sc->sc_cmap, cm, sc->sc_g300); 354 if (error) 355 return (error); 356 /* if we can handle interrupts, defer the update */ 357 if (sc->sc_ih != NULL) 358 vigra_loadcmap_deferred(sc, cm->index, cm->count); 359 else 360 vigra_loadcmap_immediate(sc, cm->index, cm->count); 361 break; 362 363 case WSDISPLAYIO_SVIDEO: 364 case WSDISPLAYIO_GVIDEO: 365 break; 366 367 case WSDISPLAYIO_GCURPOS: 368 case WSDISPLAYIO_SCURPOS: 369 case WSDISPLAYIO_GCURMAX: 370 case WSDISPLAYIO_GCURSOR: 371 case WSDISPLAYIO_SCURSOR: 372 default: 373 return (-1); /* not supported yet */ 374 } 375 376 return (0); 377 } 378 379 /* 380 * Return the address that would map the given device at the given 381 * offset, allowing for the given protection, or return -1 for error. 382 */ 383 paddr_t 384 vigra_mmap(void *v, off_t offset, int prot) 385 { 386 struct vigra_softc *sc = v; 387 388 if (offset & PGOFSET) 389 return (-1); 390 391 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) { 392 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr, 393 offset, prot, BUS_SPACE_MAP_LINEAR)); 394 } 395 396 return (-1); 397 } 398 399 void 400 vigra_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) 401 { 402 struct vigra_softc *sc = v; 403 404 if (sc->sc_g300) { 405 sc->sc_cmap.cm_map[index][3] = r; 406 sc->sc_cmap.cm_map[index][2] = g; 407 sc->sc_cmap.cm_map[index][1] = b; 408 } else { 409 sc->sc_cmap.cm_map[index][3] = b; 410 sc->sc_cmap.cm_map[index][2] = g; 411 sc->sc_cmap.cm_map[index][1] = r; 412 } 413 sc->sc_cmap.cm_map[index][0] = 0; /* no alpha channel */ 414 415 vigra_loadcmap_immediate(sc, index, 1); 416 } 417 418 int 419 vigra_getcmap(union vigracmap *cm, struct wsdisplay_cmap *rcm, int g300) 420 { 421 u_int index = rcm->index, count = rcm->count, i; 422 int error; 423 424 if (index >= 256 || count > 256 - index) 425 return (EINVAL); 426 427 if (g300) { 428 for (i = 0; i < count; i++) { 429 if ((error = copyout(&cm->cm_map[index + i][3], 430 &rcm->red[i], 1)) != 0) 431 return (error); 432 if ((error = copyout(&cm->cm_map[index + i][1], 433 &rcm->blue[i], 1)) != 0) 434 return (error); 435 } 436 } else { 437 for (i = 0; i < count; i++) { 438 if ((error = copyout(&cm->cm_map[index + i][1], 439 &rcm->red[i], 1)) != 0) 440 return (error); 441 if ((error = copyout(&cm->cm_map[index + i][3], 442 &rcm->blue[i], 1)) != 0) 443 return (error); 444 } 445 } 446 447 for (i = 0; i < count; i++) { 448 if ((error = copyout(&cm->cm_map[index + i][2], 449 &rcm->green[i], 1)) != 0) 450 return (error); 451 } 452 return (0); 453 } 454 455 int 456 vigra_putcmap(union vigracmap *cm, struct wsdisplay_cmap *rcm, int g300) 457 { 458 u_int index = rcm->index, count = rcm->count, i; 459 int error; 460 461 if (index >= 256 || count > 256 - index) 462 return (EINVAL); 463 464 if (g300) { 465 for (i = 0; i < count; i++) { 466 if ((error = copyin(&rcm->red[i], 467 &cm->cm_map[index + i][3], 1)) != 0) 468 return (error); 469 if ((error = copyin(&rcm->blue[i], 470 &cm->cm_map[index + i][1], 1)) != 0) 471 return (error); 472 } 473 } else { 474 for (i = 0; i < count; i++) { 475 if ((error = copyin(&rcm->red[i], 476 &cm->cm_map[index + i][1], 1)) != 0) 477 return (error); 478 if ((error = copyin(&rcm->blue[i], 479 &cm->cm_map[index + i][3], 1)) != 0) 480 return (error); 481 } 482 } 483 484 for (i = 0; i < count; i++) { 485 if ((error = copyin(&rcm->green[i], 486 &cm->cm_map[index + i][2], 1)) != 0) 487 return (error); 488 cm->cm_map[index + i][0] = 0; /* no alpha channel */ 489 } 490 return (0); 491 } 492 493 void 494 vigra_loadcmap_immediate(struct vigra_softc *sc, int start, int ncolors) 495 { 496 u_int32_t *colp = &sc->sc_cmap.cm_chip[start]; 497 volatile u_int32_t *lutp; 498 499 if (sc->sc_g300) 500 lutp = &(sc->sc_ramdac->g300.cmap[start]); 501 else 502 lutp = &(sc->sc_ramdac->g335.cmap[start]); 503 504 while (--ncolors >= 0) 505 *lutp++ = *colp++; 506 } 507 508 static __inline__ void 509 vigra_loadcmap_deferred(struct vigra_softc *sc, u_int start, u_int ncolors) 510 { 511 512 sc->sc_regs->imr = 1; 513 } 514 515 void 516 vigra_burner(void *v, u_int on, u_int flags) 517 { 518 struct vigra_softc *sc = v; 519 520 if (on) { 521 sc->sc_regs->bcr = 0; 522 } else { 523 sc->sc_regs->bcr = 1; 524 } 525 } 526 527 int 528 vigra_intr(void *v) 529 { 530 struct vigra_softc *sc = v; 531 532 if (sc->sc_regs->imr == 0 || 533 !ISSET(sc->sc_regs->g3sr, STATUS_INTR)) { 534 /* Not expecting an interrupt, it's not for us. */ 535 return (0); 536 } 537 538 /* Acknowledge the interrupt and disable it. */ 539 sc->sc_regs->imr = 0; 540 541 vigra_loadcmap_immediate(sc, 0, 256); 542 543 return (1); 544 } 545