1 /* $OpenBSD: vgafb.c,v 1.58 2009/06/02 18:51:03 kettenis 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/systm.h> 36 #include <sys/device.h> 37 #include <sys/errno.h> 38 #include <sys/ioctl.h> 39 #include <sys/malloc.h> 40 #include <sys/pciio.h> 41 42 #include <uvm/uvm_extern.h> 43 44 #include <machine/autoconf.h> 45 #include <machine/bus.h> 46 #include <machine/intr.h> 47 #include <machine/openfirm.h> 48 49 #include <dev/pci/pcidevs.h> 50 #include <dev/pci/pcireg.h> 51 #include <dev/pci/pcivar.h> 52 53 #include <dev/wscons/wsconsio.h> 54 #include <dev/wscons/wsdisplayvar.h> 55 #include <dev/rasops/rasops.h> 56 57 #include <machine/fbvar.h> 58 59 struct vgafb_softc { 60 struct sunfb sc_sunfb; 61 int sc_nscreens; 62 int sc_node, sc_ofhandle; 63 bus_space_tag_t sc_mem_t; 64 bus_space_tag_t sc_io_t; 65 pcitag_t sc_pcitag; 66 bus_space_handle_t sc_mem_h; 67 bus_addr_t sc_io_addr, sc_mem_addr, sc_mmio_addr; 68 bus_size_t sc_io_size, sc_mem_size, sc_mmio_size; 69 int sc_console; 70 u_int sc_mode; 71 u_int8_t sc_cmap_red[256]; 72 u_int8_t sc_cmap_green[256]; 73 u_int8_t sc_cmap_blue[256]; 74 }; 75 76 int vgafb_mapregs(struct vgafb_softc *, struct pci_attach_args *); 77 int vgafb_rommap(struct vgafb_softc *, struct pci_attach_args *); 78 int vgafb_ioctl(void *, u_long, caddr_t, int, struct proc *); 79 paddr_t vgafb_mmap(void *, off_t, int); 80 int vgafb_is_console(int); 81 int vgafb_getcmap(struct vgafb_softc *, struct wsdisplay_cmap *); 82 int vgafb_putcmap(struct vgafb_softc *, struct wsdisplay_cmap *); 83 void vgafb_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t); 84 85 struct wsdisplay_accessops vgafb_accessops = { 86 vgafb_ioctl, 87 vgafb_mmap, 88 NULL, /* alloc_screen */ 89 NULL, /* free_screen */ 90 NULL, /* show_screen */ 91 NULL, /* load_font */ 92 NULL, /* scrollback */ 93 NULL, /* getchar */ 94 NULL, /* burner */ 95 NULL /* pollc */ 96 }; 97 98 int vgafbmatch(struct device *, void *, void *); 99 void vgafbattach(struct device *, struct device *, void *); 100 101 struct cfattach vgafb_ca = { 102 sizeof (struct vgafb_softc), vgafbmatch, vgafbattach 103 }; 104 105 struct cfdriver vgafb_cd = { 106 NULL, "vgafb", DV_DULL 107 }; 108 109 #ifdef APERTURE 110 extern int allowaperture; 111 #endif 112 113 int 114 vgafbmatch(parent, vcf, aux) 115 struct device *parent; 116 void *vcf, *aux; 117 { 118 struct pci_attach_args *pa = aux; 119 int node; 120 121 /* 122 * Do not match on Expert3D devices, which are driven by ifb(4). 123 */ 124 if (ifb_ident(aux) != 0) 125 return (0); 126 127 /* 128 * XXX Non-console devices do not get configured by the PROM, 129 * XXX so do not attach them yet. 130 */ 131 node = PCITAG_NODE(pa->pa_tag); 132 if (!vgafb_is_console(node)) 133 return (0); 134 135 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_PREHISTORIC && 136 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_PREHISTORIC_VGA) 137 return (1); 138 139 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && 140 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA) 141 return (1); 142 143 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && 144 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_MISC) 145 return (1); 146 147 return (0); 148 } 149 150 void 151 vgafbattach(parent, self, aux) 152 struct device *parent, *self; 153 void *aux; 154 { 155 struct vgafb_softc *sc = (struct vgafb_softc *)self; 156 struct pci_attach_args *pa = aux; 157 158 sc->sc_mem_t = pa->pa_memt; 159 sc->sc_io_t = pa->pa_iot; 160 sc->sc_node = PCITAG_NODE(pa->pa_tag); 161 sc->sc_pcitag = pa->pa_tag; 162 163 printf("\n"); 164 165 if (vgafb_mapregs(sc, pa)) 166 return; 167 168 sc->sc_console = vgafb_is_console(sc->sc_node); 169 170 fb_setsize(&sc->sc_sunfb, 8, 1152, 900, sc->sc_node, 0); 171 if (sc->sc_sunfb.sf_depth == 24) { 172 sc->sc_sunfb.sf_depth = 32; 173 sc->sc_sunfb.sf_linebytes = 174 (sc->sc_sunfb.sf_depth / 8) * sc->sc_sunfb.sf_width; 175 sc->sc_sunfb.sf_fbsize = 176 sc->sc_sunfb.sf_height * sc->sc_sunfb.sf_linebytes; 177 } 178 179 sc->sc_sunfb.sf_ro.ri_bits = (void *)bus_space_vaddr(sc->sc_mem_t, 180 sc->sc_mem_h); 181 sc->sc_sunfb.sf_ro.ri_hw = sc; 182 183 fbwscons_init(&sc->sc_sunfb, 184 RI_BSWAP | (sc->sc_console ? 0 : RI_FORCEMONO), sc->sc_console); 185 186 if (sc->sc_console) { 187 sc->sc_ofhandle = OF_stdout(); 188 fbwscons_setcolormap(&sc->sc_sunfb, vgafb_setcolor); 189 fbwscons_console_init(&sc->sc_sunfb, -1); 190 } else { 191 /* sc->sc_ofhandle = PCITAG_NODE(sc->sc_pcitag); */ 192 } 193 194 fbwscons_attach(&sc->sc_sunfb, &vgafb_accessops, sc->sc_console); 195 } 196 197 int 198 vgafb_ioctl(v, cmd, data, flags, p) 199 void *v; 200 u_long cmd; 201 caddr_t data; 202 int flags; 203 struct proc *p; 204 { 205 struct vgafb_softc *sc = v; 206 struct wsdisplay_fbinfo *wdf; 207 struct pcisel *sel; 208 209 switch (cmd) { 210 case WSDISPLAYIO_GTYPE: 211 *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN; 212 break; 213 case WSDISPLAYIO_SMODE: 214 sc->sc_mode = *(u_int *)data; 215 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) { 216 if (sc->sc_console) /* XXX needs sc_ofhandle */ 217 fbwscons_setcolormap(&sc->sc_sunfb, 218 vgafb_setcolor); 219 } 220 break; 221 case WSDISPLAYIO_GINFO: 222 wdf = (void *)data; 223 wdf->height = sc->sc_sunfb.sf_height; 224 wdf->width = sc->sc_sunfb.sf_width; 225 wdf->depth = sc->sc_sunfb.sf_depth; 226 wdf->cmsize = 256; 227 break; 228 case WSDISPLAYIO_GETSUPPORTEDDEPTH: 229 if (sc->sc_sunfb.sf_depth == 32) 230 *(u_int *)data = WSDISPLAYIO_DEPTH_24_32; 231 else 232 return (-1); 233 break; 234 case WSDISPLAYIO_LINEBYTES: 235 *(u_int *)data = sc->sc_sunfb.sf_linebytes; 236 break; 237 238 case WSDISPLAYIO_GETCMAP: 239 if (sc->sc_console == 0) 240 return (EINVAL); 241 return vgafb_getcmap(sc, (struct wsdisplay_cmap *)data); 242 case WSDISPLAYIO_PUTCMAP: 243 if (sc->sc_console == 0) 244 return (EINVAL); 245 return vgafb_putcmap(sc, (struct wsdisplay_cmap *)data); 246 247 case WSDISPLAYIO_GPCIID: 248 sel = (struct pcisel *)data; 249 sel->pc_bus = PCITAG_BUS(sc->sc_pcitag); 250 sel->pc_dev = PCITAG_DEV(sc->sc_pcitag); 251 sel->pc_func = PCITAG_FUN(sc->sc_pcitag); 252 break; 253 254 case WSDISPLAYIO_SVIDEO: 255 case WSDISPLAYIO_GVIDEO: 256 break; 257 258 case WSDISPLAYIO_GCURPOS: 259 case WSDISPLAYIO_SCURPOS: 260 case WSDISPLAYIO_GCURMAX: 261 case WSDISPLAYIO_GCURSOR: 262 case WSDISPLAYIO_SCURSOR: 263 default: 264 return -1; /* not supported yet */ 265 } 266 267 return (0); 268 } 269 270 int 271 vgafb_getcmap(sc, cm) 272 struct vgafb_softc *sc; 273 struct wsdisplay_cmap *cm; 274 { 275 u_int index = cm->index; 276 u_int count = cm->count; 277 int error; 278 279 if (index >= 256 || count > 256 - index) 280 return (EINVAL); 281 282 error = copyout(&sc->sc_cmap_red[index], cm->red, count); 283 if (error) 284 return (error); 285 error = copyout(&sc->sc_cmap_green[index], cm->green, count); 286 if (error) 287 return (error); 288 error = copyout(&sc->sc_cmap_blue[index], cm->blue, count); 289 if (error) 290 return (error); 291 return (0); 292 } 293 294 int 295 vgafb_putcmap(sc, cm) 296 struct vgafb_softc *sc; 297 struct wsdisplay_cmap *cm; 298 { 299 u_int index = cm->index; 300 u_int count = cm->count; 301 u_int i; 302 int error; 303 u_char *r, *g, *b; 304 305 if (index >= 256 || count > 256 - index) 306 return (EINVAL); 307 308 if ((error = copyin(cm->red, &sc->sc_cmap_red[index], count)) != 0) 309 return (error); 310 if ((error = copyin(cm->green, &sc->sc_cmap_green[index], count)) != 0) 311 return (error); 312 if ((error = copyin(cm->blue, &sc->sc_cmap_blue[index], count)) != 0) 313 return (error); 314 315 r = &sc->sc_cmap_red[index]; 316 g = &sc->sc_cmap_green[index]; 317 b = &sc->sc_cmap_blue[index]; 318 319 for (i = 0; i < count; i++) { 320 OF_call_method("color!", sc->sc_ofhandle, 4, 0, *r, *g, *b, 321 index); 322 r++, g++, b++, index++; 323 } 324 return (0); 325 } 326 327 void 328 vgafb_setcolor(v, index, r, g, b) 329 void *v; 330 u_int index; 331 u_int8_t r, g, b; 332 { 333 struct vgafb_softc *sc = v; 334 335 sc->sc_cmap_red[index] = r; 336 sc->sc_cmap_green[index] = g; 337 sc->sc_cmap_blue[index] = b; 338 OF_call_method("color!", sc->sc_ofhandle, 4, 0, r, g, b, index); 339 } 340 341 paddr_t 342 vgafb_mmap(v, off, prot) 343 void *v; 344 off_t off; 345 int prot; 346 { 347 struct vgafb_softc *sc = v; 348 349 if (off & PGOFSET) 350 return (-1); 351 352 switch (sc->sc_mode) { 353 case WSDISPLAYIO_MODE_MAPPED: 354 #ifdef APERTURE 355 if (allowaperture == 0) 356 return (-1); 357 #endif 358 359 if (sc->sc_mmio_size == 0) 360 return (-1); 361 362 if (off >= sc->sc_mem_addr && 363 off < (sc->sc_mem_addr + sc->sc_mem_size)) 364 return (bus_space_mmap(sc->sc_mem_t, 365 sc->sc_mem_addr, off - sc->sc_mem_addr, 366 prot, BUS_SPACE_MAP_LINEAR)); 367 368 if (off >= sc->sc_mmio_addr && 369 off < (sc->sc_mmio_addr + sc->sc_mmio_size)) 370 return (bus_space_mmap(sc->sc_mem_t, 371 sc->sc_mmio_addr, off - sc->sc_mmio_addr, 372 prot, BUS_SPACE_MAP_LINEAR)); 373 break; 374 375 case WSDISPLAYIO_MODE_DUMBFB: 376 if (off >= 0 && off < sc->sc_mem_size) 377 return (bus_space_mmap(sc->sc_mem_t, sc->sc_mem_addr, 378 off, prot, BUS_SPACE_MAP_LINEAR)); 379 break; 380 } 381 382 return (-1); 383 } 384 385 int 386 vgafb_is_console(node) 387 int node; 388 { 389 extern int fbnode; 390 391 return (fbnode == node); 392 } 393 394 int 395 vgafb_mapregs(sc, pa) 396 struct vgafb_softc *sc; 397 struct pci_attach_args *pa; 398 { 399 bus_addr_t ba; 400 bus_size_t bs; 401 int hasio = 0, hasmem = 0, hasmmio = 0; 402 u_int32_t i, cf; 403 int rv; 404 405 for (i = PCI_MAPREG_START; i <= PCI_MAPREG_PPB_END; i += 4) { 406 cf = pci_conf_read(pa->pa_pc, pa->pa_tag, i); 407 if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) { 408 if (hasio) 409 continue; 410 rv = pci_io_find(pa->pa_pc, pa->pa_tag, i, 411 &sc->sc_io_addr, &sc->sc_io_size); 412 if (rv != 0) { 413 if (rv != ENOENT) 414 printf("%s: failed to find io at 0x%x\n", 415 sc->sc_sunfb.sf_dev.dv_xname, i); 416 continue; 417 } 418 hasio = 1; 419 } else { 420 /* Memory mapping... frame memory or mmio? */ 421 rv = pci_mem_find(pa->pa_pc, pa->pa_tag, i, 422 &ba, &bs, NULL); 423 if (rv != 0) { 424 if (rv != ENOENT) 425 printf("%s: failed to find mem at 0x%x\n", 426 sc->sc_sunfb.sf_dev.dv_xname, i); 427 continue; 428 } 429 430 if (bs == 0 /* || ba == 0 */) { 431 /* ignore this entry */ 432 } else if (hasmem == 0) { 433 /* 434 * first memory slot found goes into memory, 435 * this is for the case of no mmio 436 */ 437 sc->sc_mem_addr = ba; 438 sc->sc_mem_size = bs; 439 hasmem = 1; 440 } else { 441 /* 442 * Oh, we have a second `memory' 443 * region, is this region the vga memory 444 * or mmio, we guess that memory is 445 * the larger of the two. 446 */ 447 if (sc->sc_mem_size >= bs) { 448 /* this is the mmio */ 449 sc->sc_mmio_addr = ba; 450 /* ATI driver maps 0x80000 mmio, grr */ 451 if (bs < 0x80000) { 452 bs = 0x80000; 453 } 454 sc->sc_mmio_size = bs; 455 hasmmio = 1; 456 } else { 457 /* this is the memory */ 458 sc->sc_mmio_addr = sc->sc_mem_addr; 459 sc->sc_mmio_size = sc->sc_mem_size; 460 sc->sc_mem_addr = ba; 461 sc->sc_mem_size = bs; 462 /* ATI driver maps 0x80000 mmio, grr */ 463 if (sc->sc_mmio_size < 0x80000) { 464 sc->sc_mmio_size = 0x80000; 465 } 466 } 467 } 468 } 469 } 470 471 if (hasmem != 0) { 472 if (bus_space_map(pa->pa_memt, sc->sc_mem_addr, sc->sc_mem_size, 473 0, &sc->sc_mem_h)) { 474 printf("%s: can't map mem space\n", 475 sc->sc_sunfb.sf_dev.dv_xname); 476 return (1); 477 } 478 } 479 480 /* failure to initialize io ports should not prevent attachment */ 481 if (hasmem == 0) { 482 printf("%s: could not find memory space\n", 483 sc->sc_sunfb.sf_dev.dv_xname); 484 return (1); 485 } 486 487 #ifdef DIAGNOSTIC 488 if (hasmmio == 0) { 489 printf("%s: WARNING: no mmio space configured\n", 490 sc->sc_sunfb.sf_dev.dv_xname); 491 } 492 #endif 493 494 return (0); 495 } 496