xref: /openbsd-src/sys/dev/pci/vga_pci.c (revision 850e275390052b330d93020bf619a739a3c277ac)
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