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