1 /* $OpenBSD: vga_pci.c,v 1.37 2008/08/12 23:10:41 oga Exp $ */ 2 /* $NetBSD: vga_pci.c,v 1.3 1998/06/08 06:55:58 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 2001 Wasabi Systems, Inc. 6 * All rights reserved. 7 * 8 * Written by Frank van der Linden for Wasabi Systems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed for the NetBSD Project by 21 * Wasabi Systems, Inc. 22 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 23 * or promote products derived from this software without specific prior 24 * written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 /* 39 * Copyright (c) 1995, 1996 Carnegie-Mellon University. 40 * All rights reserved. 41 * 42 * Author: Chris G. Demetriou 43 * 44 * Permission to use, copy, modify and distribute this software and 45 * its documentation is hereby granted, provided that both the copyright 46 * notice and this permission notice appear in all copies of the 47 * software, derivative works or modified versions, and any portions 48 * thereof, and that both notices appear in supporting documentation. 49 * 50 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 51 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 52 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 53 * 54 * Carnegie Mellon requests users of this software to return to 55 * 56 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 57 * School of Computer Science 58 * Carnegie Mellon University 59 * Pittsburgh PA 15213-3890 60 * 61 * any improvements or extensions that they make and grant Carnegie the 62 * rights to redistribute these changes. 63 */ 64 65 #include "vga.h" 66 67 #include <sys/param.h> 68 #include <sys/systm.h> 69 #include <sys/kernel.h> 70 #include <sys/device.h> 71 #include <sys/malloc.h> 72 #include <sys/agpio.h> 73 74 #include <uvm/uvm.h> 75 76 #include <machine/bus.h> 77 78 #include <dev/pci/pcireg.h> 79 #include <dev/pci/pcivar.h> 80 #include <dev/pci/pcidevs.h> 81 82 #include <dev/pci/agpvar.h> 83 84 #include <dev/ic/mc6845reg.h> 85 #include <dev/ic/pcdisplayvar.h> 86 #include <dev/ic/vgareg.h> 87 #include <dev/ic/vgavar.h> 88 #include <dev/pci/vga_pcivar.h> 89 90 91 #include <dev/wscons/wsconsio.h> 92 #include <dev/wscons/wsdisplayvar.h> 93 94 #ifdef VESAFB 95 #include <dev/vesa/vesabiosvar.h> 96 #endif 97 98 #include "agp.h" 99 #include "drmbase.h" 100 101 int vga_pci_match(struct device *, void *, void *); 102 void vga_pci_attach(struct device *, struct device *, void *); 103 paddr_t vga_pci_mmap(void* v, off_t off, int prot); 104 void vga_pci_bar_init(struct vga_pci_softc *, struct pci_attach_args *); 105 106 #if NAGP > 0 107 int agpsubmatch(struct device *, void *, void *); 108 int agpbus_print(void *, const char *); 109 #endif 110 #if NDRMBASE > 0 111 int drmsubmatch(struct device *, void *, void *); 112 int vga_drm_print(void *, const char *); 113 #endif 114 115 #ifdef VESAFB 116 int vesafb_putcmap(struct vga_pci_softc *, struct wsdisplay_cmap *); 117 int vesafb_getcmap(struct vga_pci_softc *, struct wsdisplay_cmap *); 118 #endif 119 120 struct cfattach vga_pci_ca = { 121 sizeof(struct vga_pci_softc), vga_pci_match, vga_pci_attach, 122 }; 123 124 #if NAGP > 0 125 struct pci_attach_args agp_pchb_pa; 126 int agp_pchb_pa_set = 0; 127 128 void 129 agp_set_pchb(struct pci_attach_args *pa) 130 { 131 if (!agp_pchb_pa_set) { 132 memcpy(&agp_pchb_pa, pa, sizeof *pa); 133 agp_pchb_pa_set++; 134 } 135 } 136 #endif 137 138 int 139 vga_pci_match(struct device *parent, void *match, void *aux) 140 { 141 struct pci_attach_args *pa = aux; 142 143 if (DEVICE_IS_VGA_PCI(pa->pa_class) == 0) 144 return (0); 145 146 /* check whether it is disabled by firmware */ 147 if ((pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG) 148 & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)) 149 != (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)) 150 return (0); 151 152 /* If it's the console, we have a winner! */ 153 if (vga_is_console(pa->pa_iot, WSDISPLAY_TYPE_PCIVGA)) 154 return (1); 155 156 /* 157 * If we might match, make sure that the card actually looks OK. 158 */ 159 if (!vga_common_probe(pa->pa_iot, pa->pa_memt)) 160 return (0); 161 162 return (1); 163 } 164 165 void 166 vga_pci_attach(struct device *parent, struct device *self, void *aux) 167 { 168 struct pci_attach_args *pa = aux; 169 #if NAGP >0 170 struct agpbus_attach_args aba; 171 #endif 172 pcireg_t reg; 173 struct vga_pci_softc *sc = (struct vga_pci_softc *)self; 174 175 /* 176 * Enable bus master; X might need this for accelerated graphics. 177 */ 178 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); 179 reg |= PCI_COMMAND_MASTER_ENABLE; 180 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, reg); 181 182 #ifdef VESAFB 183 if (vesabios_softc != NULL && vesabios_softc->sc_nmodes > 0) { 184 sc->sc_textmode = vesafb_get_mode(sc); 185 printf(", vesafb\n"); 186 vga_extended_attach(self, pa->pa_iot, pa->pa_memt, 187 WSDISPLAY_TYPE_PCIVGA, vga_pci_mmap); 188 return; 189 } 190 #endif 191 printf("\n"); 192 vga_common_attach(self, pa->pa_iot, pa->pa_memt, 193 WSDISPLAY_TYPE_PCIVGA); 194 195 vga_pci_bar_init(sc, pa); 196 197 #if NAGP > 0 198 /* 199 * attach agp here instead of pchb so it can share mappings 200 * with the DRM 201 */ 202 if (agp_pchb_pa_set) { 203 aba.apa_pci_args = agp_pchb_pa; 204 memcpy(&aba.apa_vga_args, pa, sizeof(struct pci_attach_args)); 205 config_found_sm(self, &aba, agpbus_print, agpsubmatch); 206 207 } 208 #endif 209 210 #if NDRMBASE > 0 211 config_found_sm(self, aux, NULL, drmsubmatch); 212 #endif 213 } 214 215 #if NAGP > 0 216 int 217 agpsubmatch(struct device *parent, void *match, void *aux) 218 { 219 extern struct cfdriver agp_cd; 220 struct cfdata *cf = match; 221 222 /* only allow agp to attach */ 223 if (cf->cf_driver == &agp_cd) 224 return ((*cf->cf_attach->ca_match)(parent, match, aux)); 225 return (0); 226 } 227 228 int 229 agpbus_print(void *vaa, const char *pnp) 230 { 231 if (pnp) 232 printf("agp at %s", pnp); 233 return (UNCONF); 234 } 235 #endif 236 237 #if NDRMBASE > 0 238 int 239 drmsubmatch(struct device *parent, void *match, void *aux) 240 { 241 struct cfdata *cf = match; 242 struct cfdriver *cd; 243 size_t len = 0; 244 char *sm; 245 246 cd = cf->cf_driver; 247 248 /* is this a *drm device? */ 249 len = strlen(cd->cd_name); 250 sm = cd->cd_name + len - 3; 251 if (strncmp(sm, "drm", 3) == 0) 252 return ((*cf->cf_attach->ca_match)(parent, match, aux)); 253 254 return (0); 255 } 256 #endif 257 258 paddr_t 259 vga_pci_mmap(void *v, off_t off, int prot) 260 { 261 #ifdef VESAFB 262 struct vga_config *vc = (struct vga_config *)v; 263 struct vga_pci_softc *sc = (struct vga_pci_softc *)vc->vc_softc; 264 265 if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB) { 266 if (off < 0 || off > vesabios_softc->sc_size) 267 return (-1); 268 return atop(sc->sc_base + off); 269 } 270 #endif 271 return -1; 272 } 273 274 int 275 vga_pci_cnattach(bus_space_tag_t iot, bus_space_tag_t memt, 276 pci_chipset_tag_t pc, int bus, int device, int function) 277 { 278 return (vga_cnattach(iot, memt, WSDISPLAY_TYPE_PCIVGA, 0)); 279 } 280 281 int 282 vga_pci_ioctl(void *v, u_long cmd, caddr_t addr, int flag, struct proc *pb) 283 { 284 int error = 0; 285 #ifdef VESAFB 286 struct vga_config *vc = (struct vga_config *)v; 287 struct vga_pci_softc *sc = (struct vga_pci_softc *)vc->vc_softc; 288 struct wsdisplay_fbinfo *wdf; 289 struct wsdisplay_gfx_mode *gfxmode; 290 int mode; 291 #endif 292 293 switch (cmd) { 294 #ifdef VESAFB 295 case WSDISPLAYIO_SMODE: 296 mode = *(u_int *)addr; 297 switch (mode) { 298 case WSDISPLAYIO_MODE_EMUL: 299 /* back to text mode */ 300 vesafb_set_mode(sc, sc->sc_textmode); 301 sc->sc_mode = mode; 302 break; 303 case WSDISPLAYIO_MODE_DUMBFB: 304 if (sc->sc_gfxmode == -1) 305 return (-1); 306 vesafb_set_mode(sc, sc->sc_gfxmode); 307 sc->sc_mode = mode; 308 break; 309 default: 310 error = -1; 311 } 312 break; 313 case WSDISPLAYIO_GINFO: 314 if (sc->sc_gfxmode == -1) 315 return (-1); 316 wdf = (void *)addr; 317 wdf->height = sc->sc_height; 318 wdf->width = sc->sc_width; 319 wdf->depth = sc->sc_depth; 320 wdf->cmsize = 256; 321 break; 322 323 case WSDISPLAYIO_LINEBYTES: 324 if (sc->sc_gfxmode == -1) 325 return (-1); 326 *(u_int *)addr = sc->sc_linebytes; 327 break; 328 329 case WSDISPLAYIO_SVIDEO: 330 case WSDISPLAYIO_GVIDEO: 331 break; 332 case WSDISPLAYIO_GETCMAP: 333 if (sc->sc_depth == 8) 334 error = vesafb_getcmap(sc, 335 (struct wsdisplay_cmap *)addr); 336 break; 337 338 case WSDISPLAYIO_PUTCMAP: 339 if (sc->sc_depth == 8) 340 error = vesafb_putcmap(sc, 341 (struct wsdisplay_cmap *)addr); 342 break; 343 344 case WSDISPLAYIO_GETSUPPORTEDDEPTH: 345 *(int *)addr = vesafb_get_supported_depth(sc); 346 break; 347 348 case WSDISPLAYIO_SETGFXMODE: 349 gfxmode = (struct wsdisplay_gfx_mode *)addr; 350 sc->sc_gfxmode = vesafb_find_mode(sc, gfxmode->width, 351 gfxmode->height, gfxmode->depth); 352 if (sc->sc_gfxmode == -1) 353 error = -1; 354 break; 355 356 #endif 357 default: 358 error = ENOTTY; 359 } 360 361 return (error); 362 } 363 364 #ifdef notyet 365 void 366 vga_pci_close(void *v) 367 { 368 } 369 #endif 370 371 /* 372 * Prepare dev->bars to be used for information. we do this at startup 373 * so we can do the whole array at once, dealing with 64-bit BARs correctly. 374 */ 375 void 376 vga_pci_bar_init(struct vga_pci_softc *dev, struct pci_attach_args *pa) 377 { 378 pcireg_t type; 379 int addr = PCI_MAPREG_START, i = 0; 380 memcpy(&dev->pa, pa, sizeof(dev->pa)); 381 382 while (i < VGA_PCI_MAX_BARS) { 383 dev->bars[i] = malloc(sizeof((*dev->bars[i])), M_DEVBUF, 384 M_NOWAIT | M_ZERO); 385 if (dev->bars[i] == NULL) { 386 return; 387 } 388 389 dev->bars[i]->addr = addr; 390 391 type = dev->bars[i]->maptype = pci_mapreg_type(pa->pa_pc, 392 pa->pa_tag, addr); 393 if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, addr, 394 dev->bars[i]->maptype, &dev->bars[i]->base, 395 &dev->bars[i]->maxsize, &dev->bars[i]->flags) != 0) { 396 free(dev->bars[i], M_DEVBUF); 397 dev->bars[i] = NULL; 398 } 399 400 if (type == PCI_MAPREG_MEM_TYPE_64BIT) { 401 addr += 8; 402 i += 2; 403 } else { 404 addr+=4; 405 ++i; 406 } 407 } 408 } 409 410 /* 411 * Get the vga_pci_bar struct for the address in question. returns NULL if 412 * invalid BAR is passed. 413 */ 414 struct vga_pci_bar* 415 vga_pci_bar_info(struct vga_pci_softc *dev, int no) 416 { 417 if (dev == NULL || no > VGA_PCI_MAX_BARS) 418 return (NULL); 419 return (dev->bars[no]); 420 } 421 422 /* 423 * map the BAR in question, returning the vga_pci_bar struct in case any more 424 * processing needs to be done. Returns NULL on failure. Can be called multiple 425 * times. 426 */ 427 struct vga_pci_bar* 428 vga_pci_bar_map(struct vga_pci_softc *dev, int addr, bus_size_t size, 429 int busflags) 430 { 431 struct vga_pci_bar *bar = NULL; 432 int i; 433 434 if (dev == NULL) 435 return (NULL); 436 437 for (i = 0; i < VGA_PCI_MAX_BARS; i++) { 438 if (dev->bars[i] && dev->bars[i]->addr == addr) { 439 bar = dev->bars[i]; 440 break; 441 } 442 } 443 if (bar == NULL) { 444 printf("vga_pci_bar_map: given invalid address 0x%x\n", addr); 445 return (NULL); 446 } 447 448 if (bar->mapped == 0) { 449 if (pci_mapreg_map(&dev->pa, bar->addr, bar->maptype, 450 bar->flags | busflags, &bar->bst, &bar->bsh, NULL, 451 &bar->size, size)) { 452 printf("vga_pci_bar_map: can't map bar 0x%x\n", addr); 453 return (NULL); 454 } 455 } 456 457 bar->mapped++; 458 return (bar); 459 } 460 461 /* 462 * "unmap" the BAR referred to by argument. If more than one place has mapped it 463 * we just decrement the reference counter so nothing untoward happens. 464 */ 465 void 466 vga_pci_bar_unmap(struct vga_pci_bar *bar) 467 { 468 if (bar != NULL && bar->mapped != 0) { 469 if (--bar->mapped == 0) 470 bus_space_unmap(bar->bst, bar->bsh, bar->size); 471 } 472 } 473