1 /* $OpenBSD: vigra.c,v 1.12 2013/10/20 20:07:31 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 .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 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->cmsize = 256; 334 break; 335 case WSDISPLAYIO_LINEBYTES: 336 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 337 break; 338 339 case WSDISPLAYIO_GETCMAP: 340 cm = (struct wsdisplay_cmap *)data; 341 error = vigra_getcmap(&sc->sc_cmap, cm, sc->sc_g300); 342 if (error) 343 return (error); 344 break; 345 case WSDISPLAYIO_PUTCMAP: 346 cm = (struct wsdisplay_cmap *)data; 347 error = vigra_putcmap(&sc->sc_cmap, cm, sc->sc_g300); 348 if (error) 349 return (error); 350 /* if we can handle interrupts, defer the update */ 351 if (sc->sc_ih != NULL) 352 vigra_loadcmap_deferred(sc, cm->index, cm->count); 353 else 354 vigra_loadcmap_immediate(sc, cm->index, cm->count); 355 break; 356 357 case WSDISPLAYIO_SVIDEO: 358 case WSDISPLAYIO_GVIDEO: 359 break; 360 361 case WSDISPLAYIO_GCURPOS: 362 case WSDISPLAYIO_SCURPOS: 363 case WSDISPLAYIO_GCURMAX: 364 case WSDISPLAYIO_GCURSOR: 365 case WSDISPLAYIO_SCURSOR: 366 default: 367 return (-1); /* not supported yet */ 368 } 369 370 return (0); 371 } 372 373 /* 374 * Return the address that would map the given device at the given 375 * offset, allowing for the given protection, or return -1 for error. 376 */ 377 paddr_t 378 vigra_mmap(void *v, off_t offset, int prot) 379 { 380 struct vigra_softc *sc = v; 381 382 if (offset & PGOFSET) 383 return (-1); 384 385 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) { 386 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr, 387 offset, prot, BUS_SPACE_MAP_LINEAR)); 388 } 389 390 return (-1); 391 } 392 393 void 394 vigra_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b) 395 { 396 struct vigra_softc *sc = v; 397 398 if (sc->sc_g300) { 399 sc->sc_cmap.cm_map[index][3] = r; 400 sc->sc_cmap.cm_map[index][2] = g; 401 sc->sc_cmap.cm_map[index][1] = b; 402 } else { 403 sc->sc_cmap.cm_map[index][3] = b; 404 sc->sc_cmap.cm_map[index][2] = g; 405 sc->sc_cmap.cm_map[index][1] = r; 406 } 407 sc->sc_cmap.cm_map[index][0] = 0; /* no alpha channel */ 408 409 vigra_loadcmap_immediate(sc, index, 1); 410 } 411 412 int 413 vigra_getcmap(union vigracmap *cm, struct wsdisplay_cmap *rcm, int g300) 414 { 415 u_int index = rcm->index, count = rcm->count, i; 416 int error; 417 418 if (index >= 256 || count > 256 - index) 419 return (EINVAL); 420 421 if (g300) { 422 for (i = 0; i < count; i++) { 423 if ((error = copyout(&cm->cm_map[index + i][3], 424 &rcm->red[i], 1)) != 0) 425 return (error); 426 if ((error = copyout(&cm->cm_map[index + i][1], 427 &rcm->blue[i], 1)) != 0) 428 return (error); 429 } 430 } else { 431 for (i = 0; i < count; i++) { 432 if ((error = copyout(&cm->cm_map[index + i][1], 433 &rcm->red[i], 1)) != 0) 434 return (error); 435 if ((error = copyout(&cm->cm_map[index + i][3], 436 &rcm->blue[i], 1)) != 0) 437 return (error); 438 } 439 } 440 441 for (i = 0; i < count; i++) { 442 if ((error = copyout(&cm->cm_map[index + i][2], 443 &rcm->green[i], 1)) != 0) 444 return (error); 445 } 446 return (0); 447 } 448 449 int 450 vigra_putcmap(union vigracmap *cm, struct wsdisplay_cmap *rcm, int g300) 451 { 452 u_int index = rcm->index, count = rcm->count, i; 453 int error; 454 455 if (index >= 256 || count > 256 - index) 456 return (EINVAL); 457 458 if (g300) { 459 for (i = 0; i < count; i++) { 460 if ((error = copyin(&rcm->red[i], 461 &cm->cm_map[index + i][3], 1)) != 0) 462 return (error); 463 if ((error = copyin(&rcm->blue[i], 464 &cm->cm_map[index + i][1], 1)) != 0) 465 return (error); 466 } 467 } else { 468 for (i = 0; i < count; i++) { 469 if ((error = copyin(&rcm->red[i], 470 &cm->cm_map[index + i][1], 1)) != 0) 471 return (error); 472 if ((error = copyin(&rcm->blue[i], 473 &cm->cm_map[index + i][3], 1)) != 0) 474 return (error); 475 } 476 } 477 478 for (i = 0; i < count; i++) { 479 if ((error = copyin(&rcm->green[i], 480 &cm->cm_map[index + i][2], 1)) != 0) 481 return (error); 482 cm->cm_map[index + i][0] = 0; /* no alpha channel */ 483 } 484 return (0); 485 } 486 487 void 488 vigra_loadcmap_immediate(struct vigra_softc *sc, int start, int ncolors) 489 { 490 u_int32_t *colp = &sc->sc_cmap.cm_chip[start]; 491 volatile u_int32_t *lutp; 492 493 if (sc->sc_g300) 494 lutp = &(sc->sc_ramdac->g300.cmap[start]); 495 else 496 lutp = &(sc->sc_ramdac->g335.cmap[start]); 497 498 while (--ncolors >= 0) 499 *lutp++ = *colp++; 500 } 501 502 static __inline__ void 503 vigra_loadcmap_deferred(struct vigra_softc *sc, u_int start, u_int ncolors) 504 { 505 506 sc->sc_regs->imr = 1; 507 } 508 509 void 510 vigra_burner(void *v, u_int on, u_int flags) 511 { 512 struct vigra_softc *sc = v; 513 514 if (on) { 515 sc->sc_regs->bcr = 0; 516 } else { 517 sc->sc_regs->bcr = 1; 518 } 519 } 520 521 int 522 vigra_intr(void *v) 523 { 524 struct vigra_softc *sc = v; 525 526 if (sc->sc_regs->imr == 0 || 527 !ISSET(sc->sc_regs->g3sr, STATUS_INTR)) { 528 /* Not expecting an interrupt, it's not for us. */ 529 return (0); 530 } 531 532 /* Acknowledge the interrupt and disable it. */ 533 sc->sc_regs->imr = 0; 534 535 vigra_loadcmap_immediate(sc, 0, 256); 536 537 return (1); 538 } 539