xref: /openbsd/sys/dev/pci/vga_pci.c (revision 404b540a)
1 /* $OpenBSD: vga_pci.c,v 1.44 2009/06/06 04:38:18 pirofti 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 X86EMU
95 #include <machine/vga_post.h>
96 #endif
97 
98 #ifdef VESAFB
99 #include <dev/vesa/vesabiosvar.h>
100 #endif
101 
102 #include "intagp.h"
103 #include "drm.h"
104 
105 int	vga_pci_match(struct device *, void *, void *);
106 void	vga_pci_attach(struct device *, struct device *, void *);
107 paddr_t	vga_pci_mmap(void* v, off_t off, int prot);
108 void	vga_pci_bar_init(struct vga_pci_softc *, struct pci_attach_args *);
109 
110 #if NINTAGP > 0
111 int	intagpsubmatch(struct device *, void *, void *);
112 int	intagp_print(void *, const char *);
113 #endif
114 #if NDRM > 0
115 int	drmsubmatch(struct device *, void *, void *);
116 int	vga_drm_print(void *, const char *);
117 #endif
118 
119 #ifdef VESAFB
120 int vesafb_putcmap(struct vga_pci_softc *, struct wsdisplay_cmap *);
121 int vesafb_getcmap(struct vga_pci_softc *, struct wsdisplay_cmap *);
122 #endif
123 
124 
125 /*
126  * Function pointers for wsconsctl parameter handling.
127  * XXX These should be per-softc, but right now we only attach
128  * XXX a single vga@pci instance, so this will do.
129  */
130 int	(*ws_get_param)(struct wsdisplay_param *);
131 int	(*ws_set_param)(struct wsdisplay_param *);
132 
133 
134 struct cfattach vga_pci_ca = {
135 	sizeof(struct vga_pci_softc), vga_pci_match, vga_pci_attach,
136 };
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 	pcireg_t reg;
170 	struct vga_pci_softc *sc = (struct vga_pci_softc *)self;
171 
172 	/*
173 	 * Enable bus master; X might need this for accelerated graphics.
174 	 */
175 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
176 	reg |= PCI_COMMAND_MASTER_ENABLE;
177 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, reg);
178 
179 #ifdef VESAFB
180 	if (vesabios_softc != NULL && vesabios_softc->sc_nmodes > 0) {
181 		sc->sc_textmode = vesafb_get_mode(sc);
182 		printf(", vesafb\n");
183 		vga_extended_attach(self, pa->pa_iot, pa->pa_memt,
184 		    WSDISPLAY_TYPE_PCIVGA, vga_pci_mmap);
185 		return;
186 	}
187 #endif
188 	printf("\n");
189 	vga_common_attach(self, pa->pa_iot, pa->pa_memt,
190 	    WSDISPLAY_TYPE_PCIVGA);
191 
192 	vga_pci_bar_init(sc, pa);
193 
194 #ifdef X86EMU
195 	if ((sc->sc_posth = vga_post_init(pa->pa_bus, pa->pa_device,
196 	    pa->pa_function)) == NULL)
197 		printf("couldn't set up vga POST handler\n");
198 #endif
199 
200 #if NINTAGP > 0
201 	/*
202 	 * attach intagp here instead of pchb so it can share mappings
203 	 * with the DRM.
204 	 */
205 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) {
206 		config_found_sm(self, aux, intagp_print, intagpsubmatch);
207 
208 	}
209 #endif
210 
211 #if NDRM > 0
212 	config_found_sm(self, aux, NULL, drmsubmatch);
213 #endif
214 }
215 
216 #if NINTAGP > 0
217 int
218 intagpsubmatch(struct device *parent, void *match, void *aux)
219 {
220 	extern struct cfdriver intagp_cd;
221 	struct cfdata *cf = match;
222 
223 	/* only allow intagp to attach */
224 	if (cf->cf_driver == &intagp_cd)
225 		return ((*cf->cf_attach->ca_match)(parent, match, aux));
226 	return (0);
227 }
228 
229 int
230 intagp_print(void *vaa, const char *pnp)
231 {
232 	if (pnp)
233 		printf("intagp at %s", pnp);
234 	return (UNCONF);
235 }
236 #endif
237 
238 #if NDRM > 0
239 int
240 drmsubmatch(struct device *parent, void *match, void *aux)
241 {
242 	struct cfdata *cf = match;
243 	struct cfdriver *cd;
244 	size_t len = 0;
245 	char *sm;
246 
247 	cd = cf->cf_driver;
248 
249 	/* is this a *drm device? */
250 	len = strlen(cd->cd_name);
251 	sm = cd->cd_name + len - 3;
252 	if (strncmp(sm, "drm", 3) == 0)
253 		return ((*cf->cf_attach->ca_match)(parent, match, aux));
254 
255 	return (0);
256 }
257 #endif
258 
259 paddr_t
260 vga_pci_mmap(void *v, off_t off, int prot)
261 {
262 #ifdef VESAFB
263 	struct vga_config *vc = (struct vga_config *)v;
264 	struct vga_pci_softc *sc = (struct vga_pci_softc *)vc->vc_softc;
265 
266 	if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB) {
267 		if (off < 0 || off > vesabios_softc->sc_size)
268 			return (-1);
269 		return atop(sc->sc_base + off);
270 	}
271 #endif
272 	return -1;
273 }
274 
275 int
276 vga_pci_cnattach(bus_space_tag_t iot, bus_space_tag_t memt,
277     pci_chipset_tag_t pc, int bus, int device, int function)
278 {
279 	return (vga_cnattach(iot, memt, WSDISPLAY_TYPE_PCIVGA, 0));
280 }
281 
282 int
283 vga_pci_ioctl(void *v, u_long cmd, caddr_t addr, int flag, struct proc *pb)
284 {
285 	int error = 0;
286 #ifdef VESAFB
287 	struct vga_config *vc = (struct vga_config *)v;
288 	struct vga_pci_softc *sc = (struct vga_pci_softc *)vc->vc_softc;
289 	struct wsdisplay_fbinfo *wdf;
290 	struct wsdisplay_gfx_mode *gfxmode;
291 	int mode;
292 #endif
293 
294 	switch (cmd) {
295 #ifdef VESAFB
296 	case WSDISPLAYIO_SMODE:
297 		mode = *(u_int *)addr;
298 		switch (mode) {
299 		case WSDISPLAYIO_MODE_EMUL:
300 			/* back to text mode */
301 			vesafb_set_mode(sc, sc->sc_textmode);
302 			sc->sc_mode = mode;
303 			break;
304 		case WSDISPLAYIO_MODE_DUMBFB:
305 			if (sc->sc_gfxmode == -1)
306 				return (-1);
307 			vesafb_set_mode(sc, sc->sc_gfxmode);
308 			sc->sc_mode = mode;
309 			break;
310 		default:
311 			error = -1;
312 		}
313 		break;
314 	case WSDISPLAYIO_GINFO:
315 		if (sc->sc_gfxmode == -1)
316 			return (-1);
317 		wdf = (void *)addr;
318 		wdf->height = sc->sc_height;
319 		wdf->width = sc->sc_width;
320 		wdf->depth = sc->sc_depth;
321 		wdf->cmsize = 256;
322 		break;
323 
324 	case WSDISPLAYIO_LINEBYTES:
325 		if (sc->sc_gfxmode == -1)
326 			return (-1);
327 		*(u_int *)addr = sc->sc_linebytes;
328 		break;
329 
330 	case WSDISPLAYIO_SVIDEO:
331 	case WSDISPLAYIO_GVIDEO:
332 		break;
333 	case WSDISPLAYIO_GETCMAP:
334 		if (sc->sc_depth == 8)
335 			error = vesafb_getcmap(sc,
336 			    (struct wsdisplay_cmap *)addr);
337 		break;
338 
339 	case WSDISPLAYIO_PUTCMAP:
340 		if (sc->sc_depth == 8)
341 			error = vesafb_putcmap(sc,
342 			    (struct wsdisplay_cmap *)addr);
343 		break;
344 
345 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
346 		*(int *)addr = vesafb_get_supported_depth(sc);
347 		break;
348 
349 	case WSDISPLAYIO_SETGFXMODE:
350 		gfxmode = (struct wsdisplay_gfx_mode *)addr;
351 		sc->sc_gfxmode = vesafb_find_mode(sc, gfxmode->width,
352 		    gfxmode->height, gfxmode->depth);
353 		if (sc->sc_gfxmode == -1)
354 			error = -1;
355 		break;
356 
357 #endif
358 	case WSDISPLAYIO_GETPARAM:
359 		if (ws_get_param != NULL)
360 			return (*ws_get_param)((struct wsdisplay_param *)addr);
361 		else
362 			error = ENOTTY;
363 		break;
364 	case WSDISPLAYIO_SETPARAM:
365 		if (ws_set_param != NULL)
366 			return (*ws_set_param)((struct wsdisplay_param *)addr);
367 		else
368 			error = ENOTTY;
369 		break;
370 	default:
371 		error = ENOTTY;
372 	}
373 
374 	return (error);
375 }
376 
377 #ifdef notyet
378 void
379 vga_pci_close(void *v)
380 {
381 }
382 #endif
383 
384 /*
385  * Prepare dev->bars to be used for information. we do this at startup
386  * so we can do the whole array at once, dealing with 64-bit BARs correctly.
387  */
388 void
389 vga_pci_bar_init(struct vga_pci_softc *dev, struct pci_attach_args *pa)
390 {
391 	pcireg_t type;
392 	int addr = PCI_MAPREG_START, i = 0;
393 	memcpy(&dev->pa, pa, sizeof(dev->pa));
394 
395 	while (i < VGA_PCI_MAX_BARS) {
396 		dev->bars[i] = malloc(sizeof((*dev->bars[i])), M_DEVBUF,
397 		    M_NOWAIT | M_ZERO);
398 		if (dev->bars[i] == NULL) {
399 			return;
400 		}
401 
402 		dev->bars[i]->addr = addr;
403 
404 		type = dev->bars[i]->maptype = pci_mapreg_type(pa->pa_pc,
405 		    pa->pa_tag, addr);
406 		if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, addr,
407 		    dev->bars[i]->maptype, &dev->bars[i]->base,
408 		    &dev->bars[i]->maxsize, &dev->bars[i]->flags) != 0) {
409 			free(dev->bars[i], M_DEVBUF);
410 			dev->bars[i] = NULL;
411 		}
412 
413 		if (type == PCI_MAPREG_MEM_TYPE_64BIT) {
414 			addr += 8;
415 			i += 2;
416 		} else {
417 			addr+=4;
418 			++i;
419 		}
420 	}
421 }
422 
423 /*
424  * Get the vga_pci_bar struct for the address in question. returns NULL if
425  * invalid BAR is passed.
426  */
427 struct vga_pci_bar*
428 vga_pci_bar_info(struct vga_pci_softc *dev, int no)
429 {
430 	if (dev == NULL || no >= VGA_PCI_MAX_BARS)
431 		return (NULL);
432 	return (dev->bars[no]);
433 }
434 
435 /*
436  * map the BAR in question, returning the vga_pci_bar struct in case any more
437  * processing needs to be done. Returns NULL on failure. Can be called multiple
438  * times.
439  */
440 struct vga_pci_bar*
441 vga_pci_bar_map(struct vga_pci_softc *dev, int addr, bus_size_t size,
442     int busflags)
443 {
444 	struct vga_pci_bar *bar = NULL;
445 	int i;
446 
447 	if (dev == NULL)
448 		return (NULL);
449 
450 	for (i = 0; i < VGA_PCI_MAX_BARS; i++) {
451 		if (dev->bars[i] && dev->bars[i]->addr == addr) {
452 			bar = dev->bars[i];
453 			break;
454 		}
455 	}
456 	if (bar == NULL) {
457 		printf("vga_pci_bar_map: given invalid address 0x%x\n", addr);
458 		return (NULL);
459 	}
460 
461 	if (bar->mapped == 0) {
462 		if (pci_mapreg_map(&dev->pa, bar->addr, bar->maptype,
463 		    bar->flags | busflags, &bar->bst, &bar->bsh, NULL,
464 		    &bar->size, size)) {
465 			printf("vga_pci_bar_map: can't map bar 0x%x\n", addr);
466 			return (NULL);
467 		}
468 	}
469 
470 	bar->mapped++;
471 	return (bar);
472 }
473 
474 /*
475  * "unmap" the BAR referred to by argument. If more than one place has mapped it
476  * we just decrement the reference counter so nothing untoward happens.
477  */
478 void
479 vga_pci_bar_unmap(struct vga_pci_bar *bar)
480 {
481 	if (bar != NULL && bar->mapped != 0) {
482 		if (--bar->mapped == 0)
483 			bus_space_unmap(bar->bst, bar->bsh, bar->size);
484 	}
485 }
486