xref: /openbsd-src/sys/dev/pci/vga_pci.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $OpenBSD: vga_pci.c,v 1.39 2008/11/22 21:26:48 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 "intagp.h"
99 #include "drm.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 NINTAGP > 0
107 int	intagpsubmatch(struct device *, void *, void *);
108 int	intagp_print(void *, const char *);
109 #endif
110 #if NDRM > 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 int
125 vga_pci_match(struct device *parent, void *match, void *aux)
126 {
127 	struct pci_attach_args *pa = aux;
128 
129 	if (DEVICE_IS_VGA_PCI(pa->pa_class) == 0)
130 		return (0);
131 
132 	/* check whether it is disabled by firmware */
133 	if ((pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG)
134 	    & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
135 	    != (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
136 		return (0);
137 
138 	/* If it's the console, we have a winner! */
139 	if (vga_is_console(pa->pa_iot, WSDISPLAY_TYPE_PCIVGA))
140 		return (1);
141 
142 	/*
143 	 * If we might match, make sure that the card actually looks OK.
144 	 */
145 	if (!vga_common_probe(pa->pa_iot, pa->pa_memt))
146 		return (0);
147 
148 	return (1);
149 }
150 
151 void
152 vga_pci_attach(struct device *parent, struct device *self, void *aux)
153 {
154 	struct pci_attach_args *pa = aux;
155 	pcireg_t reg;
156 	struct vga_pci_softc *sc = (struct vga_pci_softc *)self;
157 
158 	/*
159 	 * Enable bus master; X might need this for accelerated graphics.
160 	 */
161 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
162 	reg |= PCI_COMMAND_MASTER_ENABLE;
163 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, reg);
164 
165 #ifdef VESAFB
166 	if (vesabios_softc != NULL && vesabios_softc->sc_nmodes > 0) {
167 		sc->sc_textmode = vesafb_get_mode(sc);
168 		printf(", vesafb\n");
169 		vga_extended_attach(self, pa->pa_iot, pa->pa_memt,
170 		    WSDISPLAY_TYPE_PCIVGA, vga_pci_mmap);
171 		return;
172 	}
173 #endif
174 	printf("\n");
175 	vga_common_attach(self, pa->pa_iot, pa->pa_memt,
176 	    WSDISPLAY_TYPE_PCIVGA);
177 
178 	vga_pci_bar_init(sc, pa);
179 
180 #if NINTAGP > 0
181 	/*
182 	 * attach intagp here instead of pchb so it can share mappings
183 	 * with the DRM.
184 	 */
185 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) {
186 		config_found_sm(self, aux, intagp_print, intagpsubmatch);
187 
188 	}
189 #endif
190 
191 #if NDRM > 0
192 	config_found_sm(self, aux, NULL, drmsubmatch);
193 #endif
194 }
195 
196 #if NINTAGP > 0
197 int
198 intagpsubmatch(struct device *parent, void *match, void *aux)
199 {
200 	extern struct cfdriver intagp_cd;
201 	struct cfdata *cf = match;
202 
203 	/* only allow intagp to attach */
204 	if (cf->cf_driver == &intagp_cd)
205 		return ((*cf->cf_attach->ca_match)(parent, match, aux));
206 	return (0);
207 }
208 
209 int
210 intagp_print(void *vaa, const char *pnp)
211 {
212 	if (pnp)
213 		printf("intagp at %s", pnp);
214 	return (UNCONF);
215 }
216 #endif
217 
218 #if NDRM > 0
219 int
220 drmsubmatch(struct device *parent, void *match, void *aux)
221 {
222 	struct cfdata *cf = match;
223 	struct cfdriver *cd;
224 	size_t len = 0;
225 	char *sm;
226 
227 	cd = cf->cf_driver;
228 
229 	/* is this a *drm device? */
230 	len = strlen(cd->cd_name);
231 	sm = cd->cd_name + len - 3;
232 	if (strncmp(sm, "drm", 3) == 0)
233 		return ((*cf->cf_attach->ca_match)(parent, match, aux));
234 
235 	return (0);
236 }
237 #endif
238 
239 paddr_t
240 vga_pci_mmap(void *v, off_t off, int prot)
241 {
242 #ifdef VESAFB
243 	struct vga_config *vc = (struct vga_config *)v;
244 	struct vga_pci_softc *sc = (struct vga_pci_softc *)vc->vc_softc;
245 
246 	if (sc->sc_mode == WSDISPLAYIO_MODE_DUMBFB) {
247 		if (off < 0 || off > vesabios_softc->sc_size)
248 			return (-1);
249 		return atop(sc->sc_base + off);
250 	}
251 #endif
252 	return -1;
253 }
254 
255 int
256 vga_pci_cnattach(bus_space_tag_t iot, bus_space_tag_t memt,
257     pci_chipset_tag_t pc, int bus, int device, int function)
258 {
259 	return (vga_cnattach(iot, memt, WSDISPLAY_TYPE_PCIVGA, 0));
260 }
261 
262 int
263 vga_pci_ioctl(void *v, u_long cmd, caddr_t addr, int flag, struct proc *pb)
264 {
265 	int error = 0;
266 #ifdef VESAFB
267 	struct vga_config *vc = (struct vga_config *)v;
268 	struct vga_pci_softc *sc = (struct vga_pci_softc *)vc->vc_softc;
269 	struct wsdisplay_fbinfo *wdf;
270 	struct wsdisplay_gfx_mode *gfxmode;
271 	int mode;
272 #endif
273 
274 	switch (cmd) {
275 #ifdef VESAFB
276 	case WSDISPLAYIO_SMODE:
277 		mode = *(u_int *)addr;
278 		switch (mode) {
279 		case WSDISPLAYIO_MODE_EMUL:
280 			/* back to text mode */
281 			vesafb_set_mode(sc, sc->sc_textmode);
282 			sc->sc_mode = mode;
283 			break;
284 		case WSDISPLAYIO_MODE_DUMBFB:
285 			if (sc->sc_gfxmode == -1)
286 				return (-1);
287 			vesafb_set_mode(sc, sc->sc_gfxmode);
288 			sc->sc_mode = mode;
289 			break;
290 		default:
291 			error = -1;
292 		}
293 		break;
294 	case WSDISPLAYIO_GINFO:
295 		if (sc->sc_gfxmode == -1)
296 			return (-1);
297 		wdf = (void *)addr;
298 		wdf->height = sc->sc_height;
299 		wdf->width = sc->sc_width;
300 		wdf->depth = sc->sc_depth;
301 		wdf->cmsize = 256;
302 		break;
303 
304 	case WSDISPLAYIO_LINEBYTES:
305 		if (sc->sc_gfxmode == -1)
306 			return (-1);
307 		*(u_int *)addr = sc->sc_linebytes;
308 		break;
309 
310 	case WSDISPLAYIO_SVIDEO:
311 	case WSDISPLAYIO_GVIDEO:
312 		break;
313 	case WSDISPLAYIO_GETCMAP:
314 		if (sc->sc_depth == 8)
315 			error = vesafb_getcmap(sc,
316 			    (struct wsdisplay_cmap *)addr);
317 		break;
318 
319 	case WSDISPLAYIO_PUTCMAP:
320 		if (sc->sc_depth == 8)
321 			error = vesafb_putcmap(sc,
322 			    (struct wsdisplay_cmap *)addr);
323 		break;
324 
325 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
326 		*(int *)addr = vesafb_get_supported_depth(sc);
327 		break;
328 
329 	case WSDISPLAYIO_SETGFXMODE:
330 		gfxmode = (struct wsdisplay_gfx_mode *)addr;
331 		sc->sc_gfxmode = vesafb_find_mode(sc, gfxmode->width,
332 		    gfxmode->height, gfxmode->depth);
333 		if (sc->sc_gfxmode == -1)
334 			error = -1;
335 		break;
336 
337 #endif
338 	default:
339 		error = ENOTTY;
340 	}
341 
342 	return (error);
343 }
344 
345 #ifdef notyet
346 void
347 vga_pci_close(void *v)
348 {
349 }
350 #endif
351 
352 /*
353  * Prepare dev->bars to be used for information. we do this at startup
354  * so we can do the whole array at once, dealing with 64-bit BARs correctly.
355  */
356 void
357 vga_pci_bar_init(struct vga_pci_softc *dev, struct pci_attach_args *pa)
358 {
359 	pcireg_t type;
360 	int addr = PCI_MAPREG_START, i = 0;
361 	memcpy(&dev->pa, pa, sizeof(dev->pa));
362 
363 	while (i < VGA_PCI_MAX_BARS) {
364 		dev->bars[i] = malloc(sizeof((*dev->bars[i])), M_DEVBUF,
365 		    M_NOWAIT | M_ZERO);
366 		if (dev->bars[i] == NULL) {
367 			return;
368 		}
369 
370 		dev->bars[i]->addr = addr;
371 
372 		type = dev->bars[i]->maptype = pci_mapreg_type(pa->pa_pc,
373 		    pa->pa_tag, addr);
374 		if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, addr,
375 		    dev->bars[i]->maptype, &dev->bars[i]->base,
376 		    &dev->bars[i]->maxsize, &dev->bars[i]->flags) != 0) {
377 			free(dev->bars[i], M_DEVBUF);
378 			dev->bars[i] = NULL;
379 		}
380 
381 		if (type == PCI_MAPREG_MEM_TYPE_64BIT) {
382 			addr += 8;
383 			i += 2;
384 		} else {
385 			addr+=4;
386 			++i;
387 		}
388 	}
389 }
390 
391 /*
392  * Get the vga_pci_bar struct for the address in question. returns NULL if
393  * invalid BAR is passed.
394  */
395 struct vga_pci_bar*
396 vga_pci_bar_info(struct vga_pci_softc *dev, int no)
397 {
398 	if (dev == NULL || no > VGA_PCI_MAX_BARS)
399 		return (NULL);
400 	return (dev->bars[no]);
401 }
402 
403 /*
404  * map the BAR in question, returning the vga_pci_bar struct in case any more
405  * processing needs to be done. Returns NULL on failure. Can be called multiple
406  * times.
407  */
408 struct vga_pci_bar*
409 vga_pci_bar_map(struct vga_pci_softc *dev, int addr, bus_size_t size,
410     int busflags)
411 {
412 	struct vga_pci_bar *bar = NULL;
413 	int i;
414 
415 	if (dev == NULL)
416 		return (NULL);
417 
418 	for (i = 0; i < VGA_PCI_MAX_BARS; i++) {
419 		if (dev->bars[i] && dev->bars[i]->addr == addr) {
420 			bar = dev->bars[i];
421 			break;
422 		}
423 	}
424 	if (bar == NULL) {
425 		printf("vga_pci_bar_map: given invalid address 0x%x\n", addr);
426 		return (NULL);
427 	}
428 
429 	if (bar->mapped == 0) {
430 		if (pci_mapreg_map(&dev->pa, bar->addr, bar->maptype,
431 		    bar->flags | busflags, &bar->bst, &bar->bsh, NULL,
432 		    &bar->size, size)) {
433 			printf("vga_pci_bar_map: can't map bar 0x%x\n", addr);
434 			return (NULL);
435 		}
436 	}
437 
438 	bar->mapped++;
439 	return (bar);
440 }
441 
442 /*
443  * "unmap" the BAR referred to by argument. If more than one place has mapped it
444  * we just decrement the reference counter so nothing untoward happens.
445  */
446 void
447 vga_pci_bar_unmap(struct vga_pci_bar *bar)
448 {
449 	if (bar != NULL && bar->mapped != 0) {
450 		if (--bar->mapped == 0)
451 			bus_space_unmap(bar->bst, bar->bsh, bar->size);
452 	}
453 }
454