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