1 /* $OpenBSD: vgafb.c,v 1.34 2003/06/16 21:46:23 miod Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Jason L. Wright (jason@thought.net) 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 * Effort sponsored in part by the Defense Advanced Research Projects 29 * Agency (DARPA) and Air Force Research Laboratory, Air Force 30 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 31 * 32 */ 33 34 #include <sys/param.h> 35 #include <sys/buf.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/errno.h> 39 #include <sys/device.h> 40 #include <sys/ioctl.h> 41 #include <sys/malloc.h> 42 #include <sys/pciio.h> 43 44 #include <uvm/uvm_extern.h> 45 46 #include <machine/bus.h> 47 #include <machine/intr.h> 48 #include <machine/autoconf.h> 49 #include <machine/openfirm.h> 50 51 #include <dev/pci/pcivar.h> 52 #include <dev/wscons/wsconsio.h> 53 #include <dev/wscons/wsdisplayvar.h> 54 #include <dev/wscons/wscons_raster.h> 55 #include <dev/rasops/rasops.h> 56 #include <machine/fbvar.h> 57 58 struct vgafb_softc { 59 struct sunfb sc_sunfb; 60 int sc_nscreens; 61 int sc_node, sc_ofhandle; 62 bus_space_tag_t sc_mem_t; 63 bus_space_tag_t sc_io_t; 64 pcitag_t sc_pcitag; 65 bus_space_handle_t sc_mem_h; 66 bus_addr_t sc_io_addr, sc_mem_addr, sc_mmio_addr; 67 bus_size_t sc_io_size, sc_mem_size, sc_mmio_size; 68 pci_chipset_tag_t sc_pci_chip; 69 int sc_has_rom; 70 int sc_console; 71 u_int sc_mode; 72 u_int8_t sc_cmap_red[256]; 73 u_int8_t sc_cmap_green[256]; 74 u_int8_t sc_cmap_blue[256]; 75 int *sc_crowp, *sc_ccolp; 76 }; 77 78 struct wsscreen_descr vgafb_stdscreen = { 79 "std", 80 }; 81 82 const struct wsscreen_descr *vgafb_scrlist[] = { 83 &vgafb_stdscreen, 84 /* XXX other formats? */ 85 }; 86 87 struct wsscreen_list vgafb_screenlist = { 88 sizeof(vgafb_scrlist) / sizeof(struct wsscreen_descr *), vgafb_scrlist 89 }; 90 91 int vgafb_mapregs(struct vgafb_softc *, struct pci_attach_args *); 92 int vgafb_rommap(struct vgafb_softc *, struct pci_attach_args *); 93 int vgafb_ioctl(void *, u_long, caddr_t, int, struct proc *); 94 int vgafb_alloc_screen(void *, const struct wsscreen_descr *, void **, 95 int *, int *, long *); 96 void vgafb_free_screen(void *, void *); 97 int vgafb_show_screen(void *, void *, int, 98 void (*cb)(void *, int, int), void *); 99 paddr_t vgafb_mmap(void *, off_t, int); 100 int vgafb_is_console(int); 101 int vgafb_getcmap(struct vgafb_softc *, struct wsdisplay_cmap *); 102 int vgafb_putcmap(struct vgafb_softc *, struct wsdisplay_cmap *); 103 void vgafb_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t); 104 void vgafb_updatecursor(struct rasops_info *ri); 105 106 struct wsdisplay_accessops vgafb_accessops = { 107 vgafb_ioctl, 108 vgafb_mmap, 109 vgafb_alloc_screen, 110 vgafb_free_screen, 111 vgafb_show_screen, 112 0 /* load_font */ 113 }; 114 115 int vgafbmatch(struct device *, void *, void *); 116 void vgafbattach(struct device *, struct device *, void *); 117 118 struct cfattach vgafb_ca = { 119 sizeof (struct vgafb_softc), vgafbmatch, vgafbattach 120 }; 121 122 struct cfdriver vgafb_cd = { 123 NULL, "vgafb", DV_DULL 124 }; 125 126 int 127 vgafbmatch(parent, vcf, aux) 128 struct device *parent; 129 void *vcf, *aux; 130 { 131 struct pci_attach_args *pa = aux; 132 133 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_PREHISTORIC && 134 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_PREHISTORIC_VGA) 135 return (1); 136 137 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && 138 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA) 139 return (1); 140 141 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && 142 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_MISC) 143 return (1); 144 145 return (0); 146 } 147 148 void 149 vgafbattach(parent, self, aux) 150 struct device *parent, *self; 151 void *aux; 152 { 153 struct vgafb_softc *sc = (struct vgafb_softc *)self; 154 struct pci_attach_args *pa = aux; 155 struct wsemuldisplaydev_attach_args waa; 156 157 sc->sc_mem_t = pa->pa_memt; 158 sc->sc_io_t = pa->pa_iot; 159 sc->sc_node = PCITAG_NODE(pa->pa_tag); 160 sc->sc_pcitag = pa->pa_tag; 161 162 printf("\n"); 163 164 if (vgafb_mapregs(sc, pa)) 165 return; 166 167 sc->sc_console = vgafb_is_console(sc->sc_node); 168 169 fb_setsize(&sc->sc_sunfb, 8, 1152, 900, sc->sc_node, 0); 170 if (sc->sc_sunfb.sf_depth == 24) { 171 sc->sc_sunfb.sf_depth = 32; 172 sc->sc_sunfb.sf_linebytes = 173 (sc->sc_sunfb.sf_depth / 8) * sc->sc_sunfb.sf_width; 174 sc->sc_sunfb.sf_fbsize = 175 sc->sc_sunfb.sf_height * sc->sc_sunfb.sf_linebytes; 176 } 177 178 sc->sc_sunfb.sf_ro.ri_bits = (void *)bus_space_vaddr(sc->sc_mem_t, 179 sc->sc_mem_h); 180 sc->sc_sunfb.sf_ro.ri_hw = sc; 181 182 fbwscons_init(&sc->sc_sunfb, 183 RI_BSWAP | (sc->sc_console ? 0 : RI_CLEAR)); 184 185 vgafb_stdscreen.capabilities = sc->sc_sunfb.sf_ro.ri_caps; 186 vgafb_stdscreen.nrows = sc->sc_sunfb.sf_ro.ri_rows; 187 vgafb_stdscreen.ncols = sc->sc_sunfb.sf_ro.ri_cols; 188 vgafb_stdscreen.textops = &sc->sc_sunfb.sf_ro.ri_ops; 189 190 if (sc->sc_console) { 191 sc->sc_ofhandle = OF_stdout(); 192 fbwscons_setcolormap(&sc->sc_sunfb, vgafb_setcolor); 193 sc->sc_sunfb.sf_ro.ri_updatecursor = vgafb_updatecursor; 194 fbwscons_console_init(&sc->sc_sunfb, &vgafb_stdscreen, -1, 195 NULL); 196 } else { 197 /* sc->sc_ofhandle = XXX */ 198 } 199 200 waa.console = sc->sc_console; 201 waa.scrdata = &vgafb_screenlist; 202 waa.accessops = &vgafb_accessops; 203 waa.accesscookie = sc; 204 config_found(self, &waa, wsemuldisplaydevprint); 205 } 206 207 int 208 vgafb_ioctl(v, cmd, data, flags, p) 209 void *v; 210 u_long cmd; 211 caddr_t data; 212 int flags; 213 struct proc *p; 214 { 215 struct vgafb_softc *sc = v; 216 struct wsdisplay_fbinfo *wdf; 217 struct pcisel *sel; 218 219 switch (cmd) { 220 case WSDISPLAYIO_GTYPE: 221 *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN; 222 break; 223 case WSDISPLAYIO_SMODE: 224 sc->sc_mode = *(u_int *)data; 225 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) 226 fbwscons_setcolormap(&sc->sc_sunfb, vgafb_setcolor); 227 break; 228 case WSDISPLAYIO_GINFO: 229 wdf = (void *)data; 230 wdf->height = sc->sc_sunfb.sf_height; 231 wdf->width = sc->sc_sunfb.sf_width; 232 wdf->depth = sc->sc_sunfb.sf_depth; 233 wdf->cmsize = 256; 234 break; 235 case WSDISPLAYIO_LINEBYTES: 236 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 237 break; 238 239 case WSDISPLAYIO_GETCMAP: 240 if (sc->sc_console == 0) 241 return (EINVAL); 242 return vgafb_getcmap(sc, (struct wsdisplay_cmap *)data); 243 case WSDISPLAYIO_PUTCMAP: 244 if (sc->sc_console == 0) 245 return (EINVAL); 246 return vgafb_putcmap(sc, (struct wsdisplay_cmap *)data); 247 248 case WSDISPLAYIO_GPCIID: 249 sel = (struct pcisel *)data; 250 sel->pc_bus = PCITAG_BUS(sc->sc_pcitag); 251 sel->pc_dev = PCITAG_DEV(sc->sc_pcitag); 252 sel->pc_func = PCITAG_FUNC(sc->sc_pcitag); 253 break; 254 255 case WSDISPLAYIO_SVIDEO: 256 case WSDISPLAYIO_GVIDEO: 257 case WSDISPLAYIO_GCURPOS: 258 case WSDISPLAYIO_SCURPOS: 259 case WSDISPLAYIO_GCURMAX: 260 case WSDISPLAYIO_GCURSOR: 261 case WSDISPLAYIO_SCURSOR: 262 default: 263 return -1; /* not supported yet */ 264 } 265 266 return (0); 267 } 268 269 int 270 vgafb_getcmap(sc, cm) 271 struct vgafb_softc *sc; 272 struct wsdisplay_cmap *cm; 273 { 274 u_int index = cm->index; 275 u_int count = cm->count; 276 int error; 277 278 if (index >= 256 || count > 256 - index) 279 return (EINVAL); 280 281 error = copyout(&sc->sc_cmap_red[index], cm->red, count); 282 if (error) 283 return (error); 284 error = copyout(&sc->sc_cmap_green[index], cm->green, count); 285 if (error) 286 return (error); 287 error = copyout(&sc->sc_cmap_blue[index], cm->blue, count); 288 if (error) 289 return (error); 290 return (0); 291 } 292 293 int 294 vgafb_putcmap(sc, cm) 295 struct vgafb_softc *sc; 296 struct wsdisplay_cmap *cm; 297 { 298 u_int index = cm->index; 299 u_int count = cm->count; 300 u_int i; 301 int error; 302 u_char *r, *g, *b; 303 304 if (index >= 256 || count > 256 - index) 305 return (EINVAL); 306 307 if ((error = copyin(cm->red, &sc->sc_cmap_red[index], count)) != 0) 308 return (error); 309 if ((error = copyin(cm->green, &sc->sc_cmap_green[index], count)) != 0) 310 return (error); 311 if ((error = copyin(cm->blue, &sc->sc_cmap_blue[index], count)) != 0) 312 return (error); 313 314 r = &sc->sc_cmap_red[index]; 315 g = &sc->sc_cmap_green[index]; 316 b = &sc->sc_cmap_blue[index]; 317 318 for (i = 0; i < count; i++) { 319 OF_call_method("color!", sc->sc_ofhandle, 4, 0, *r, *g, *b, 320 index); 321 r++, g++, b++, index++; 322 } 323 return (0); 324 } 325 326 void 327 vgafb_setcolor(v, index, r, g, b) 328 void *v; 329 u_int index; 330 u_int8_t r, g, b; 331 { 332 struct vgafb_softc *sc = v; 333 334 sc->sc_cmap_red[index] = r; 335 sc->sc_cmap_green[index] = g; 336 sc->sc_cmap_blue[index] = b; 337 OF_call_method("color!", sc->sc_ofhandle, 4, 0, r, g, b, index); 338 } 339 340 int 341 vgafb_alloc_screen(v, type, cookiep, curxp, curyp, attrp) 342 void *v; 343 const struct wsscreen_descr *type; 344 void **cookiep; 345 int *curxp, *curyp; 346 long *attrp; 347 { 348 struct vgafb_softc *sc = v; 349 350 if (sc->sc_nscreens > 0) 351 return (ENOMEM); 352 353 *cookiep = &sc->sc_sunfb.sf_ro; 354 *curyp = 0; 355 *curxp = 0; 356 sc->sc_sunfb.sf_ro.ri_ops.alloc_attr(&sc->sc_sunfb.sf_ro, 357 WSCOL_BLACK, WSCOL_WHITE, WSATTR_WSCOLORS, attrp); 358 sc->sc_nscreens++; 359 return (0); 360 } 361 362 void 363 vgafb_free_screen(v, cookie) 364 void *v; 365 void *cookie; 366 { 367 struct vgafb_softc *sc = v; 368 369 sc->sc_nscreens--; 370 } 371 372 int 373 vgafb_show_screen(v, cookie, waitok, cb, cbarg) 374 void *v; 375 void *cookie; 376 int waitok; 377 void (*cb)(void *, int, int); 378 void *cbarg; 379 { 380 return (0); 381 } 382 383 paddr_t 384 vgafb_mmap(v, off, prot) 385 void *v; 386 off_t off; 387 int prot; 388 { 389 struct vgafb_softc *sc = v; 390 391 if (off & PGOFSET) 392 return (-1); 393 394 switch (sc->sc_mode) { 395 case WSDISPLAYIO_MODE_MAPPED: 396 if (off >= sc->sc_mem_addr && 397 off < (sc->sc_mem_addr + sc->sc_mem_size)) 398 return (bus_space_mmap(sc->sc_mem_t, 399 sc->sc_mem_addr, off - sc->sc_mem_addr, 400 prot, BUS_SPACE_MAP_LINEAR)); 401 402 if (off >= sc->sc_mmio_addr && 403 off < (sc->sc_mmio_addr + sc->sc_mmio_size)) 404 return (bus_space_mmap(sc->sc_mem_t, 405 sc->sc_mmio_addr, off - sc->sc_mmio_addr, 406 prot, BUS_SPACE_MAP_LINEAR)); 407 break; 408 409 case WSDISPLAYIO_MODE_DUMBFB: 410 if (off >= 0 && off < sc->sc_mem_size) 411 return (bus_space_mmap(sc->sc_mem_t, sc->sc_mem_addr, 412 off, prot, BUS_SPACE_MAP_LINEAR)); 413 break; 414 } 415 416 return (-1); 417 } 418 419 int 420 vgafb_is_console(node) 421 int node; 422 { 423 extern int fbnode; 424 425 return (fbnode == node); 426 } 427 428 int 429 vgafb_mapregs(sc, pa) 430 struct vgafb_softc *sc; 431 struct pci_attach_args *pa; 432 { 433 bus_addr_t ba; 434 bus_size_t bs; 435 int hasio = 0, hasmem = 0, hasmmio = 0; 436 u_int32_t i, cf; 437 438 for (i = PCI_MAPREG_START; i < PCI_MAPREG_END; i += 4) { 439 cf = pci_conf_read(pa->pa_pc, pa->pa_tag, i); 440 if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) { 441 if (hasio) 442 continue; 443 if (pci_io_find(pa->pa_pc, pa->pa_tag, i, 444 &sc->sc_io_addr, &sc->sc_io_size)) { 445 printf(": failed to find io at 0x%x\n", i); 446 continue; 447 } 448 hasio = 1; 449 } else { 450 /* Memory mapping... frame memory or mmio? */ 451 if (pci_mem_find(pa->pa_pc, pa->pa_tag, i, 452 &ba, &bs, NULL)) { 453 printf(": failed to find mem at 0x%x\n", i); 454 continue; 455 } 456 457 if (bs <= 0x10000) { /* mmio */ 458 if (hasmmio) 459 continue; 460 sc->sc_mmio_addr = ba; 461 sc->sc_mmio_size = bs; 462 hasmmio = 1; 463 } else { 464 if (hasmem) 465 continue; 466 if (bus_space_map(pa->pa_memt, ba, bs, 467 0, &sc->sc_mem_h)) { 468 printf(": can't map mem space\n"); 469 continue; 470 } 471 sc->sc_mem_addr = ba; 472 sc->sc_mem_size = bs; 473 hasmem = 1; 474 } 475 } 476 } 477 478 if (hasmmio == 0 || hasmem == 0 || hasio == 0) { 479 printf(": failed to find all ports\n"); 480 goto fail; 481 } 482 483 return (0); 484 485 fail: 486 if (hasmem) 487 bus_space_unmap(pa->pa_memt, sc->sc_mem_h, sc->sc_mem_size); 488 return (1); 489 } 490 491 void 492 vgafb_updatecursor(ri) 493 struct rasops_info *ri; 494 { 495 struct vgafb_softc *sc = ri->ri_hw; 496 497 if (sc->sc_crowp != NULL) 498 *sc->sc_crowp = ri->ri_crow; 499 if (sc->sc_ccolp != NULL) 500 *sc->sc_ccolp = ri->ri_ccol; 501 } 502