xref: /openbsd/sys/dev/pci/agp.c (revision 8f8e4ea1)
1*8f8e4ea1Soga /* $OpenBSD: agp.c,v 1.20 2008/04/09 18:59:58 oga Exp $ */
20139788eSmatthieu /*-
30139788eSmatthieu  * Copyright (c) 2000 Doug Rabson
40139788eSmatthieu  * All rights reserved.
50139788eSmatthieu  *
60139788eSmatthieu  * Redistribution and use in source and binary forms, with or without
70139788eSmatthieu  * modification, are permitted provided that the following conditions
80139788eSmatthieu  * are met:
90139788eSmatthieu  * 1. Redistributions of source code must retain the above copyright
100139788eSmatthieu  *    notice, this list of conditions and the following disclaimer.
110139788eSmatthieu  * 2. Redistributions in binary form must reproduce the above copyright
120139788eSmatthieu  *    notice, this list of conditions and the following disclaimer in the
130139788eSmatthieu  *    documentation and/or other materials provided with the distribution.
140139788eSmatthieu  *
150139788eSmatthieu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
160139788eSmatthieu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170139788eSmatthieu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
180139788eSmatthieu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
190139788eSmatthieu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
200139788eSmatthieu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
210139788eSmatthieu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
220139788eSmatthieu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
230139788eSmatthieu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
240139788eSmatthieu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
250139788eSmatthieu  * SUCH DAMAGE.
260139788eSmatthieu  *
270139788eSmatthieu  *	$FreeBSD: src/sys/pci/agp.c,v 1.12 2001/05/19 01:28:07 alfred Exp $
280139788eSmatthieu  */
290139788eSmatthieu 
300139788eSmatthieu #include <sys/param.h>
310139788eSmatthieu #include <sys/malloc.h>
320139788eSmatthieu #include <sys/agpio.h>
330139788eSmatthieu #include <sys/fcntl.h>
340139788eSmatthieu #include <sys/ioctl.h>
350139788eSmatthieu 
360139788eSmatthieu #include <uvm/uvm.h>
370139788eSmatthieu 
380139788eSmatthieu #include <dev/pci/pcivar.h>
3940d7c10bSoga #include <dev/pci/pcidevs.h>
400139788eSmatthieu 
410139788eSmatthieu #include <dev/ic/mc6845reg.h>
420139788eSmatthieu #include <dev/ic/pcdisplayvar.h>
430139788eSmatthieu #include <dev/ic/vgareg.h>
440139788eSmatthieu #include <dev/ic/vgavar.h>
450139788eSmatthieu 
460139788eSmatthieu #include <dev/pci/agpvar.h>
470139788eSmatthieu #include <dev/pci/agpreg.h>
480139788eSmatthieu 
4940d7c10bSoga #include "agp_ali.h"
5040d7c10bSoga #include "agp_amd.h"
5140d7c10bSoga #include "agp_amd64.h"
5240d7c10bSoga #include "agp_apple.h"
5340d7c10bSoga #include "agp_i810.h"
5440d7c10bSoga #include "agp_intel.h"
5540d7c10bSoga #include "agp_sis.h"
5640d7c10bSoga #include "agp_via.h"
570139788eSmatthieu 
5840d7c10bSoga struct agp_memory *agp_find_memory(struct agp_softc *sc, int id);
5940d7c10bSoga const struct agp_product *agp_lookup(struct pci_attach_args *pa);
6040d7c10bSoga /* userland ioctl functions */
6170456743Soga int	agp_info_user(void *, agp_info *);
6270456743Soga int	agp_setup_user(void *, agp_setup *);
6370456743Soga int	agp_allocate_user(void *, agp_allocate *);
6470456743Soga int	agp_deallocate_user(void *, int);
6570456743Soga int	agp_bind_user(void *, agp_bind *);
6670456743Soga int	agp_unbind_user(void *, agp_unbind *);
6770456743Soga int	agp_acquire_helper(void *dev, enum agp_acquire_state state);
6870456743Soga int	agp_release_helper(void *dev, enum agp_acquire_state state);
6940d7c10bSoga 
7040d7c10bSoga const struct agp_product agp_products[] = {
7140d7c10bSoga #if NAGP_ALI > 0
7240d7c10bSoga 	{ PCI_VENDOR_ALI, -1, agp_ali_attach },
7340d7c10bSoga #endif
7440d7c10bSoga #if NAGP_AMD > 0
7540d7c10bSoga 	{ PCI_VENDOR_AMD, -1, agp_amd_attach },
7640d7c10bSoga #endif
7740d7c10bSoga #if NAGP_I810 > 0
78c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82810_HB, agp_i810_attach },
79c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82810_DC100_HB, agp_i810_attach },
80c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82810E_HB, agp_i810_attach },
81c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82815_HB, agp_i810_attach },
82c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82830M_HB, agp_i810_attach },
83c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82845G_HB, agp_i810_attach },
84c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82855GM_HB, agp_i810_attach },
85c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82865G_HB, agp_i810_attach },
8640d7c10bSoga 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82915G_HB, agp_i810_attach },
8740d7c10bSoga 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82915GM_HB, agp_i810_attach },
88c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82945G_HB, agp_i810_attach },
89c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82945GM_HB, agp_i810_attach },
90c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82G965_HB, agp_i810_attach },
91c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82Q965_HB, agp_i810_attach },
92c839f1b1Skettenis 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82GM965_HB, agp_i810_attach },
939671a04fSdamien 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82G33_HB, agp_i810_attach },
9440d7c10bSoga #endif
9540d7c10bSoga #if NAGP_INTEL > 0
9640d7c10bSoga 	{ PCI_VENDOR_INTEL, -1, agp_intel_attach },
9740d7c10bSoga #endif
9840d7c10bSoga #if NAGP_SIS > 0
9940d7c10bSoga 	{ PCI_VENDOR_SIS, -1, agp_sis_attach },
10040d7c10bSoga #endif
10140d7c10bSoga #if NAGP_VIA > 0
10240d7c10bSoga 	{ PCI_VENDOR_VIATECH, -1, agp_via_attach },
10340d7c10bSoga #endif
10440d7c10bSoga 	{ 0, 0, NULL }
10540d7c10bSoga };
10640d7c10bSoga 
10740d7c10bSoga 
10870456743Soga int
10940d7c10bSoga agp_probe(struct device *parent, void *match, void *aux)
11040d7c10bSoga {
11140d7c10bSoga 	struct agpbus_attach_args *aaa = aux;
11240d7c10bSoga 	struct pci_attach_args *pa = &aaa->apa_pci_args;
11340d7c10bSoga 
114f2fa622eSoga 	/* pci_args must be a pchb */
115f2fa622eSoga 	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
116f2fa622eSoga 	    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_HOST)
117f2fa622eSoga 		return (0);
118f2fa622eSoga 
11940d7c10bSoga 	if (agp_lookup(pa) == NULL)
12040d7c10bSoga 		return (0);
12140d7c10bSoga 
12240d7c10bSoga 	return (1);
12340d7c10bSoga }
1240139788eSmatthieu 
1250139788eSmatthieu void
1260139788eSmatthieu agp_attach(struct device *parent, struct device *self, void *aux)
1270139788eSmatthieu {
12840d7c10bSoga 	struct agpbus_attach_args *aaa = aux;
12940d7c10bSoga 	struct pci_attach_args *pa = &aaa->apa_pci_args;
13040d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *)self;
1310139788eSmatthieu 	const struct agp_product *ap;
1320139788eSmatthieu 	u_int memsize;
1330139788eSmatthieu 	int i, ret;
1340139788eSmatthieu 
1350139788eSmatthieu 	ap = agp_lookup(pa);
1360139788eSmatthieu 	if (ap) {
1370139788eSmatthieu 		static const int agp_max[][2] = {
1380139788eSmatthieu 			{0,		0},
1390139788eSmatthieu 			{32,		4},
1400139788eSmatthieu 			{64,		28},
1410139788eSmatthieu 			{128,		96},
1420139788eSmatthieu 			{256,		204},
1430139788eSmatthieu 			{512,		440},
1440139788eSmatthieu 			{1024,		942},
1450139788eSmatthieu 			{2048,		1920},
1460139788eSmatthieu 			{4096,		3932}
1470139788eSmatthieu 		};
1480139788eSmatthieu #define	agp_max_size	 (sizeof(agp_max)/sizeof(agp_max[0]))
1490139788eSmatthieu 
1500139788eSmatthieu 		/*
1510139788eSmatthieu 		 * Work out an upper bound for agp memory allocation. This
1520139788eSmatthieu 		 * uses a heuristic table from the Linux driver.
1530139788eSmatthieu 		 */
1540139788eSmatthieu 		memsize = ptoa(physmem) >> 20;
1550139788eSmatthieu 
1560139788eSmatthieu 		for (i = 0; i < agp_max_size && memsize > agp_max[i][0]; i++)
1570139788eSmatthieu 			;
1580139788eSmatthieu 		if (i == agp_max_size)
1590139788eSmatthieu 			i = agp_max_size - 1;
1600139788eSmatthieu 		sc->sc_maxmem = agp_max[i][1] << 20;
1610139788eSmatthieu 
1620139788eSmatthieu 		/*
1630139788eSmatthieu 		 * The lock is used to prevent re-entry to
1640139788eSmatthieu 		 * agp_generic_bind_memory() since that function can sleep.
1650139788eSmatthieu 		 */
1660139788eSmatthieu 
167f0c692fcSoga 		rw_init(&sc->sc_lock, "agplk");
1680139788eSmatthieu 
1690139788eSmatthieu 		TAILQ_INIT(&sc->sc_memory);
1700139788eSmatthieu 
1710139788eSmatthieu 		sc->sc_pcitag = pa->pa_tag;
1720139788eSmatthieu 		sc->sc_pc = pa->pa_pc;
1730139788eSmatthieu 		sc->sc_id = pa->pa_id;
1740139788eSmatthieu 		sc->sc_dmat = pa->pa_dmat;
1750139788eSmatthieu 
1760139788eSmatthieu 		pci_get_capability(sc->sc_pc, sc->sc_pcitag, PCI_CAP_AGP,
1770139788eSmatthieu 		    &sc->sc_capoff, NULL);
1780139788eSmatthieu 
1791d396234Sderaadt 		printf(": ");
18040d7c10bSoga 		ret = (*ap->ap_attach)(sc, pa);
1810139788eSmatthieu 		if (ret == 0)
1821d396234Sderaadt 			printf("aperture at 0x%lx, size 0x%lx\n",
1830139788eSmatthieu 			    (u_long)sc->sc_apaddr,
1840139788eSmatthieu 			    (u_long)AGP_GET_APERTURE(sc));
1850139788eSmatthieu 		else {
1860139788eSmatthieu 			sc->sc_chipc = NULL;
1870139788eSmatthieu 		}
1880139788eSmatthieu 	}
1890139788eSmatthieu }
1900139788eSmatthieu 
19140d7c10bSoga struct cfattach agp_ca = {
19240d7c10bSoga         sizeof (struct agp_softc), agp_probe, agp_attach,
19340d7c10bSoga 	NULL, NULL
19440d7c10bSoga };
19540d7c10bSoga 
19640d7c10bSoga struct cfdriver agp_cd = {
19740d7c10bSoga 	NULL, "agp", DV_DULL
19840d7c10bSoga };
19940d7c10bSoga 
2000139788eSmatthieu paddr_t
20140d7c10bSoga agpmmap(void *v, off_t off, int prot)
2020139788eSmatthieu {
20340d7c10bSoga 	struct agp_softc* sc = (struct agp_softc *)v;
2040139788eSmatthieu 
2050139788eSmatthieu 	if (sc->sc_apaddr) {
2060139788eSmatthieu 
2070139788eSmatthieu 		if (off > AGP_GET_APERTURE(sc))
2080139788eSmatthieu 			return (-1);
2090139788eSmatthieu 
21040d7c10bSoga 		/*
21140d7c10bSoga 		 * XXX this should use bus_space_mmap() but it's not
21240d7c10bSoga 		 * availiable on all archs.
21340d7c10bSoga 		 */
2140139788eSmatthieu 		return atop(sc->sc_apaddr + off);
2150139788eSmatthieu 	}
21640d7c10bSoga 	return (-1);
2170139788eSmatthieu }
2180139788eSmatthieu 
219e47997a5Soga int
220e47997a5Soga agpopen(dev_t dev, int oflags, int devtype, struct proc *p)
2210139788eSmatthieu {
222*8f8e4ea1Soga         struct agp_softc *sc = agp_find_device(AGPUNIT(dev));
22340d7c10bSoga 
22440d7c10bSoga         if (sc == NULL)
22540d7c10bSoga                 return (ENXIO);
22640d7c10bSoga 
22740d7c10bSoga         if (sc->sc_chipc == NULL)
22840d7c10bSoga                 return (ENXIO);
22940d7c10bSoga 
23040d7c10bSoga         if (!sc->sc_opened)
23140d7c10bSoga                 sc->sc_opened = 1;
23240d7c10bSoga         else
23340d7c10bSoga                 return (EBUSY);
23440d7c10bSoga 
23540d7c10bSoga         return (0);
23640d7c10bSoga }
23740d7c10bSoga 
23840d7c10bSoga 
23940d7c10bSoga int
24040d7c10bSoga agpioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *pb)
24140d7c10bSoga {
242*8f8e4ea1Soga 	struct agp_softc *sc = agp_find_device(AGPUNIT(dev));
24340d7c10bSoga 
24440d7c10bSoga 	if (sc ==NULL)
24540d7c10bSoga 		return (ENODEV);
2460139788eSmatthieu 
247ccb3d2ebSderaadt 	if (sc->sc_methods == NULL || sc->sc_chipc == NULL)
2481791d3edSderaadt 		return (ENXIO);
2491791d3edSderaadt 
2500139788eSmatthieu 	if (cmd != AGPIOC_INFO && !(flag & FWRITE))
2510139788eSmatthieu 		return (EPERM);
25240d7c10bSoga 
2530139788eSmatthieu 	switch(cmd) {
2540139788eSmatthieu 	case AGPIOC_INFO:
25540d7c10bSoga 		return (agp_info_user(sc, (agp_info *)addr));
2560139788eSmatthieu 
2570139788eSmatthieu 	case AGPIOC_ACQUIRE:
25840d7c10bSoga 		return (agp_acquire_helper(sc, AGP_ACQUIRE_USER));
2590139788eSmatthieu 
2600139788eSmatthieu 	case AGPIOC_RELEASE:
26140d7c10bSoga 		return (agp_release_helper(sc, AGP_ACQUIRE_USER));
2620139788eSmatthieu 
2630139788eSmatthieu 	case AGPIOC_SETUP:
26440d7c10bSoga 		return (agp_setup_user(sc, (agp_setup *)addr));
2650139788eSmatthieu 
2660139788eSmatthieu 	case AGPIOC_ALLOCATE:
26740d7c10bSoga 		return (agp_allocate_user(sc, (agp_allocate *)addr));
2680139788eSmatthieu 
2690139788eSmatthieu 	case AGPIOC_DEALLOCATE:
27040d7c10bSoga 		return (agp_deallocate_user(sc, *(int *)addr));
2710139788eSmatthieu 
2720139788eSmatthieu 	case AGPIOC_BIND:
27340d7c10bSoga 		return (agp_bind_user(sc, (agp_bind *)addr));
2740139788eSmatthieu 
2750139788eSmatthieu 	case AGPIOC_UNBIND:
27640d7c10bSoga 		return (agp_unbind_user(sc, (agp_unbind *)addr));
27740d7c10bSoga 
2780139788eSmatthieu 	default:
27940d7c10bSoga 		return (ENOTTY);
2800139788eSmatthieu 	}
2810139788eSmatthieu 
2820139788eSmatthieu }
2830139788eSmatthieu 
28440d7c10bSoga int
28540d7c10bSoga agpclose(dev_t dev, int flags, int devtype, struct proc *p)
2860139788eSmatthieu {
287*8f8e4ea1Soga 	struct agp_softc *sc = agp_find_device(AGPUNIT(dev));
2880139788eSmatthieu 	struct agp_memory *mem;
2890139788eSmatthieu 
2900139788eSmatthieu 	/*
29140d7c10bSoga          * Clear the GATT and force release on last close
2920139788eSmatthieu          */
29340d7c10bSoga 	if (sc->sc_state == AGP_ACQUIRE_USER) {
29440d7c10bSoga 		while ((mem = TAILQ_FIRST(&sc->sc_memory)) != 0) {
29540d7c10bSoga 			if (mem->am_is_bound)
2960139788eSmatthieu 				AGP_UNBIND_MEMORY(sc, mem);
2970139788eSmatthieu 			AGP_FREE_MEMORY(sc, mem);
2980139788eSmatthieu 		}
29940d7c10bSoga                 agp_release_helper(sc, AGP_ACQUIRE_USER);
3000139788eSmatthieu 	}
30140d7c10bSoga         sc->sc_opened = 0;
30240d7c10bSoga 
30340d7c10bSoga 	return (0);
30440d7c10bSoga }
3050139788eSmatthieu 
3060139788eSmatthieu struct agp_memory *
30740d7c10bSoga agp_find_memory(struct agp_softc *sc, int id)
3080139788eSmatthieu {
3090139788eSmatthieu 	struct agp_memory *mem;
3100139788eSmatthieu 
3110139788eSmatthieu 	AGP_DPF("searching for memory block %d\n", id);
3120139788eSmatthieu 	TAILQ_FOREACH(mem, &sc->sc_memory, am_link) {
3130139788eSmatthieu 		AGP_DPF("considering memory block %d\n", mem->am_id);
3140139788eSmatthieu 		if (mem->am_id == id)
3150139788eSmatthieu 			return (mem);
3160139788eSmatthieu 	}
31740d7c10bSoga 	return (0);
3180139788eSmatthieu }
3190139788eSmatthieu 
3200139788eSmatthieu const struct agp_product *
3210139788eSmatthieu agp_lookup(struct pci_attach_args *pa)
3220139788eSmatthieu {
3230139788eSmatthieu 	const struct agp_product *ap;
3240139788eSmatthieu 
3250139788eSmatthieu 	/* First find the vendor. */
3260139788eSmatthieu 	for (ap = agp_products; ap->ap_attach != NULL; ap++)
3270139788eSmatthieu 		if (ap->ap_vendor == PCI_VENDOR(pa->pa_id))
3280139788eSmatthieu 			break;
3290139788eSmatthieu 
3300139788eSmatthieu 	if (ap->ap_attach == NULL)
3310139788eSmatthieu 		return (NULL);
3320139788eSmatthieu 
3330139788eSmatthieu 	/* Now find the product within the vendor's domain. */
3340139788eSmatthieu 	for (; ap->ap_attach != NULL; ap++) {
3350139788eSmatthieu 		/* Ran out of this vendor's section of the table. */
3360139788eSmatthieu 		if (ap->ap_vendor != PCI_VENDOR(pa->pa_id))
3370139788eSmatthieu 			return (NULL);
3380139788eSmatthieu 
3390139788eSmatthieu 		if (ap->ap_product == PCI_PRODUCT(pa->pa_id))
3400139788eSmatthieu 			break;		/* Exact match. */
3410139788eSmatthieu 		if (ap->ap_product == (u_int32_t) -1)
3420139788eSmatthieu 			break;		/* Wildcard match. */
3430139788eSmatthieu 	}
3440139788eSmatthieu 
3450139788eSmatthieu 	if (ap->ap_attach == NULL)
3460139788eSmatthieu 		ap = NULL;
3470139788eSmatthieu 
3480139788eSmatthieu 	return (ap);
3490139788eSmatthieu }
3500139788eSmatthieu 
3510139788eSmatthieu int
35240d7c10bSoga agp_map_aperture(struct pci_attach_args *pa, struct agp_softc *sc, u_int32_t bar, u_int32_t memtype)
3530139788eSmatthieu {
354ac9c0c59Sbrad 	/* Find the aperture. Don't map it (yet), this would eat KVA */
35540d7c10bSoga 	if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar,
3567f10d90bSreyk 	    memtype, &sc->sc_apaddr, &sc->sc_apsize,
3570139788eSmatthieu 	    &sc->sc_apflags) != 0)
35840d7c10bSoga 		return (ENXIO);
3590139788eSmatthieu 
36040d7c10bSoga 	return (0);
3610139788eSmatthieu }
3620139788eSmatthieu 
3630c73c53fSoga u_int32_t
3640c73c53fSoga agp_generic_get_aperture(struct agp_softc *sc)
3650c73c53fSoga {
3660c73c53fSoga 	return (sc->sc_apsize);
3670c73c53fSoga }
3680c73c53fSoga 
3690c73c53fSoga int
3700c73c53fSoga agp_generic_set_aperture(struct agp_softc *sc, u_int32_t aperture)
3710c73c53fSoga {
3720c73c53fSoga 	if (aperture != AGP_GET_APERTURE(sc))
3730c73c53fSoga 		return (EINVAL);
3740c73c53fSoga 
3750c73c53fSoga 	return (0);
3760c73c53fSoga }
3770c73c53fSoga 
3780139788eSmatthieu struct agp_gatt *
37940d7c10bSoga agp_alloc_gatt(struct agp_softc *sc)
3800139788eSmatthieu {
3810139788eSmatthieu 	u_int32_t apsize = AGP_GET_APERTURE(sc);
3820139788eSmatthieu 	u_int32_t entries = apsize >> AGP_PAGE_SHIFT;
3830139788eSmatthieu 	struct agp_gatt *gatt;
3840139788eSmatthieu 	int nseg;
3850139788eSmatthieu 
38640d7c10bSoga 	gatt = malloc(sizeof(*gatt), M_AGP, M_NOWAIT | M_ZERO);
3870139788eSmatthieu 	if (!gatt)
3880139788eSmatthieu 		return (NULL);
3890139788eSmatthieu 	gatt->ag_entries = entries;
3900139788eSmatthieu 
3910139788eSmatthieu 	if (agp_alloc_dmamem(sc->sc_dmat, entries * sizeof(u_int32_t),
3920139788eSmatthieu 	    0, &gatt->ag_dmamap, (caddr_t *)&gatt->ag_virtual,
3930139788eSmatthieu 	    &gatt->ag_physical, &gatt->ag_dmaseg, 1, &nseg) != 0)
39440d7c10bSoga 		return (NULL);
3950139788eSmatthieu 
3960139788eSmatthieu 	gatt->ag_size = entries * sizeof(u_int32_t);
3970139788eSmatthieu 	memset(gatt->ag_virtual, 0, gatt->ag_size);
3980139788eSmatthieu 	agp_flush_cache();
3990139788eSmatthieu 
40040d7c10bSoga 	return (gatt);
4010139788eSmatthieu }
4020139788eSmatthieu 
4030139788eSmatthieu void
40440d7c10bSoga agp_free_gatt(struct agp_softc *sc, struct agp_gatt *gatt)
4050139788eSmatthieu {
4060139788eSmatthieu 	agp_free_dmamem(sc->sc_dmat, gatt->ag_size, gatt->ag_dmamap,
4070139788eSmatthieu 	    (caddr_t)gatt->ag_virtual, &gatt->ag_dmaseg, 1);
40840d7c10bSoga 	free(gatt, M_AGP);
4090139788eSmatthieu }
4100139788eSmatthieu 
4110139788eSmatthieu int
41240d7c10bSoga agp_generic_detach(struct agp_softc *sc)
4130139788eSmatthieu {
4140139788eSmatthieu 	agp_flush_cache();
41540d7c10bSoga 	return (0);
4160139788eSmatthieu }
4170139788eSmatthieu 
4180139788eSmatthieu int
41940d7c10bSoga agp_generic_enable(struct agp_softc *sc, u_int32_t mode)
4200139788eSmatthieu {
4210139788eSmatthieu 	pcireg_t tstatus, mstatus;
4220139788eSmatthieu 	pcireg_t command;
4230139788eSmatthieu 	int rq, sba, fw, rate, capoff;
4240139788eSmatthieu 
4250139788eSmatthieu 	if (pci_get_capability(sc->sc_pc, sc->sc_pcitag, PCI_CAP_AGP,
4260139788eSmatthieu 	    &capoff, NULL) == 0) {
4270139788eSmatthieu 		printf("agp_generic_enable: not an AGP capable device\n");
42840d7c10bSoga 		return (-1);
4290139788eSmatthieu 	}
4300139788eSmatthieu 
4310139788eSmatthieu 	tstatus = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
4320139788eSmatthieu 	    sc->sc_capoff + AGP_STATUS);
4330139788eSmatthieu 	mstatus = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
4340139788eSmatthieu 	    capoff + AGP_STATUS);
4350139788eSmatthieu 
4360139788eSmatthieu 	/* Set RQ to the min of mode, tstatus and mstatus */
4370139788eSmatthieu 	rq = AGP_MODE_GET_RQ(mode);
4380139788eSmatthieu 	if (AGP_MODE_GET_RQ(tstatus) < rq)
4390139788eSmatthieu 		rq = AGP_MODE_GET_RQ(tstatus);
4400139788eSmatthieu 	if (AGP_MODE_GET_RQ(mstatus) < rq)
4410139788eSmatthieu 		rq = AGP_MODE_GET_RQ(mstatus);
4420139788eSmatthieu 
4430139788eSmatthieu 	/* Set SBA if all three can deal with SBA */
4440139788eSmatthieu 	sba = (AGP_MODE_GET_SBA(tstatus)
4450139788eSmatthieu 	    & AGP_MODE_GET_SBA(mstatus)
4460139788eSmatthieu 	    & AGP_MODE_GET_SBA(mode));
4470139788eSmatthieu 
4480139788eSmatthieu 	/* Similar for FW */
4490139788eSmatthieu 	fw = (AGP_MODE_GET_FW(tstatus)
4500139788eSmatthieu 	    & AGP_MODE_GET_FW(mstatus)
4510139788eSmatthieu 	    & AGP_MODE_GET_FW(mode));
4520139788eSmatthieu 
4530139788eSmatthieu 	/* Figure out the max rate */
4540139788eSmatthieu 	rate = (AGP_MODE_GET_RATE(tstatus)
4550139788eSmatthieu 	    & AGP_MODE_GET_RATE(mstatus)
4560139788eSmatthieu 	    & AGP_MODE_GET_RATE(mode));
4570139788eSmatthieu 	if (rate & AGP_MODE_RATE_4x)
4580139788eSmatthieu 		rate = AGP_MODE_RATE_4x;
4590139788eSmatthieu 	else if (rate & AGP_MODE_RATE_2x)
4600139788eSmatthieu 		rate = AGP_MODE_RATE_2x;
4610139788eSmatthieu 	else
4620139788eSmatthieu 		rate = AGP_MODE_RATE_1x;
4630139788eSmatthieu 
4640139788eSmatthieu 	/* Construct the new mode word and tell the hardware  */
4650139788eSmatthieu 	command = AGP_MODE_SET_RQ(0, rq);
4660139788eSmatthieu 	command = AGP_MODE_SET_SBA(command, sba);
4670139788eSmatthieu 	command = AGP_MODE_SET_FW(command, fw);
4680139788eSmatthieu 	command = AGP_MODE_SET_RATE(command, rate);
4690139788eSmatthieu 	command = AGP_MODE_SET_AGP(command, 1);
4700139788eSmatthieu 	pci_conf_write(sc->sc_pc, sc->sc_pcitag,
4710139788eSmatthieu 	    sc->sc_capoff + AGP_COMMAND, command);
4720139788eSmatthieu 	pci_conf_write(sc->sc_pc, sc->sc_pcitag, capoff + AGP_COMMAND, command);
47340d7c10bSoga 	return (0);
4740139788eSmatthieu }
4750139788eSmatthieu 
4760139788eSmatthieu struct agp_memory *
47740d7c10bSoga agp_generic_alloc_memory(struct agp_softc *sc, int type, vsize_t size)
4780139788eSmatthieu {
4790139788eSmatthieu 	struct agp_memory *mem;
4800139788eSmatthieu 
4810139788eSmatthieu 	if (type != 0) {
4820139788eSmatthieu 		printf("agp_generic_alloc_memory: unsupported type %d\n", type);
48340d7c10bSoga 		return (0);
4840139788eSmatthieu 	}
4850139788eSmatthieu 
48640d7c10bSoga 	mem = malloc(sizeof *mem, M_AGP, M_WAITOK | M_ZERO);
4870139788eSmatthieu 
4880139788eSmatthieu 	if (bus_dmamap_create(sc->sc_dmat, size, size / PAGE_SIZE + 1,
4890139788eSmatthieu 	    size, 0, BUS_DMA_NOWAIT, &mem->am_dmamap) != 0) {
49040d7c10bSoga 		free(mem, M_AGP);
49140d7c10bSoga 		return (NULL);
4920139788eSmatthieu 	}
4930139788eSmatthieu 
4940139788eSmatthieu 	mem->am_id = sc->sc_nextid++;
4950139788eSmatthieu 	mem->am_size = size;
4960139788eSmatthieu 	TAILQ_INSERT_TAIL(&sc->sc_memory, mem, am_link);
4970139788eSmatthieu 	sc->sc_allocated += size;
4980139788eSmatthieu 
49940d7c10bSoga 	return (mem);
5000139788eSmatthieu }
5010139788eSmatthieu 
5020139788eSmatthieu int
50340d7c10bSoga agp_generic_free_memory(struct agp_softc *sc, struct agp_memory *mem)
5040139788eSmatthieu {
5050139788eSmatthieu 	if (mem->am_is_bound)
50640d7c10bSoga 		return (EBUSY);
5070139788eSmatthieu 
5080139788eSmatthieu 	sc->sc_allocated -= mem->am_size;
5090139788eSmatthieu 	TAILQ_REMOVE(&sc->sc_memory, mem, am_link);
5100139788eSmatthieu 	bus_dmamap_destroy(sc->sc_dmat, mem->am_dmamap);
51140d7c10bSoga 	free(mem, M_AGP);
51240d7c10bSoga 	return (0);
5130139788eSmatthieu }
5140139788eSmatthieu 
5150139788eSmatthieu int
51640d7c10bSoga agp_generic_bind_memory(struct agp_softc *sc, struct agp_memory *mem,
5170139788eSmatthieu 			off_t offset)
5180139788eSmatthieu {
5190139788eSmatthieu 	bus_dma_segment_t *segs, *seg;
5200139788eSmatthieu 	bus_size_t done, j;
5210139788eSmatthieu 	bus_addr_t pa;
5220139788eSmatthieu 	off_t i, k;
523eabb5cdaSdim 	int nseg, error;
5240139788eSmatthieu 
525f0c692fcSoga 	rw_enter_write(&sc->sc_lock);
5260139788eSmatthieu 
5270139788eSmatthieu 	if (mem->am_is_bound) {
5280139788eSmatthieu 		printf("AGP: memory already bound\n");
529f0c692fcSoga 		rw_exit_write(&sc->sc_lock);
53040d7c10bSoga 		return (EINVAL);
5310139788eSmatthieu 	}
5320139788eSmatthieu 
5330139788eSmatthieu 	if (offset < 0
5340139788eSmatthieu 	    || (offset & (AGP_PAGE_SIZE - 1)) != 0
5350139788eSmatthieu 	    || offset + mem->am_size > AGP_GET_APERTURE(sc)) {
5360139788eSmatthieu 		printf("AGP: binding memory at bad offset %#lx\n",
5370139788eSmatthieu 		    (unsigned long) offset);
538f0c692fcSoga 		rw_exit_write(&sc->sc_lock);
53940d7c10bSoga 		return (EINVAL);
5400139788eSmatthieu 	}
5410139788eSmatthieu 
5420139788eSmatthieu 	/*
5430139788eSmatthieu 	 * The memory here needs to be directly accessable from the
5440139788eSmatthieu 	 * AGP video card, so it should be allocated using bus_dma.
5450139788eSmatthieu 	 * However, it need not be contiguous, since individual pages
5460139788eSmatthieu 	 * are translated using the GATT.
5470139788eSmatthieu 	 */
5480139788eSmatthieu 
549eabb5cdaSdim 	nseg = (mem->am_size + PAGE_SIZE - 1) / PAGE_SIZE;
55040d7c10bSoga 	segs = malloc(nseg * sizeof *segs, M_AGP, M_WAITOK);
5510139788eSmatthieu 	if ((error = bus_dmamem_alloc(sc->sc_dmat, mem->am_size, PAGE_SIZE, 0,
5520139788eSmatthieu 	    segs, nseg, &mem->am_nseg, BUS_DMA_WAITOK)) != 0) {
55340d7c10bSoga 		free(segs, M_AGP);
554f0c692fcSoga 		rw_exit_write(&sc->sc_lock);
5550139788eSmatthieu 		AGP_DPF("bus_dmamem_alloc failed %d\n", error);
55640d7c10bSoga 		return (error);
5570139788eSmatthieu 	}
5580139788eSmatthieu 	if ((error = bus_dmamem_map(sc->sc_dmat, segs, mem->am_nseg,
5590139788eSmatthieu 	    mem->am_size, &mem->am_virtual, BUS_DMA_WAITOK)) != 0) {
5600139788eSmatthieu 		bus_dmamem_free(sc->sc_dmat, segs, mem->am_nseg);
56140d7c10bSoga 		free(segs, M_AGP);
562f0c692fcSoga 		rw_exit_write(&sc->sc_lock);
5630139788eSmatthieu 		AGP_DPF("bus_dmamem_map failed %d\n", error);
56440d7c10bSoga 		return (error);
5650139788eSmatthieu 	}
5660139788eSmatthieu 	if ((error = bus_dmamap_load(sc->sc_dmat, mem->am_dmamap,
5670139788eSmatthieu 	    mem->am_virtual, mem->am_size, NULL,
5680139788eSmatthieu 	    BUS_DMA_WAITOK)) != 0) {
5690139788eSmatthieu 		bus_dmamem_unmap(sc->sc_dmat, mem->am_virtual,
5700139788eSmatthieu 		    mem->am_size);
5710139788eSmatthieu 		bus_dmamem_free(sc->sc_dmat, segs, mem->am_nseg);
57240d7c10bSoga 		free(segs, M_AGP);
573f0c692fcSoga 		rw_exit_write(&sc->sc_lock);
5740139788eSmatthieu 		AGP_DPF("bus_dmamap_load failed %d\n", error);
57540d7c10bSoga 		return (error);
5760139788eSmatthieu 	}
5770139788eSmatthieu 	mem->am_dmaseg = segs;
5780139788eSmatthieu 
5790139788eSmatthieu 	/*
5800139788eSmatthieu 	 * Bind the individual pages and flush the chipset's
5810139788eSmatthieu 	 * TLB.
5820139788eSmatthieu 	 */
5830139788eSmatthieu 	done = 0;
5840139788eSmatthieu 	for (i = 0; i < mem->am_dmamap->dm_nsegs; i++) {
5850139788eSmatthieu 		seg = &mem->am_dmamap->dm_segs[i];
5860139788eSmatthieu 		/*
5870139788eSmatthieu 		 * Install entries in the GATT, making sure that if
5880139788eSmatthieu 		 * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not
5890139788eSmatthieu 		 * aligned to PAGE_SIZE, we don't modify too many GATT
5900139788eSmatthieu 		 * entries.
5910139788eSmatthieu 		 */
5920139788eSmatthieu 		for (j = 0; j < seg->ds_len && (done + j) < mem->am_size;
5930139788eSmatthieu 		    j += AGP_PAGE_SIZE) {
5940139788eSmatthieu 			pa = seg->ds_addr + j;
5950139788eSmatthieu 			AGP_DPF("binding offset %#lx to pa %#lx\n",
5960139788eSmatthieu 			    (unsigned long)(offset + done + j),
5970139788eSmatthieu 			    (unsigned long)pa);
5980139788eSmatthieu 			error = AGP_BIND_PAGE(sc, offset + done + j, pa);
5990139788eSmatthieu 			if (error) {
6000139788eSmatthieu 				/*
6010139788eSmatthieu 				 * Bail out. Reverse all the mappings
6020139788eSmatthieu 				 * and unwire the pages.
6030139788eSmatthieu 				 */
6040139788eSmatthieu 				for (k = 0; k < done + j; k += AGP_PAGE_SIZE)
6050139788eSmatthieu 					AGP_UNBIND_PAGE(sc, offset + k);
6060139788eSmatthieu 
6070139788eSmatthieu 				bus_dmamap_unload(sc->sc_dmat, mem->am_dmamap);
6080139788eSmatthieu 				bus_dmamem_unmap(sc->sc_dmat, mem->am_virtual,
6090139788eSmatthieu 				    mem->am_size);
6100139788eSmatthieu 				bus_dmamem_free(sc->sc_dmat, mem->am_dmaseg,
6110139788eSmatthieu 				    mem->am_nseg);
61240d7c10bSoga 				free(mem->am_dmaseg, M_AGP);
613f0c692fcSoga 				rw_exit_write(&sc->sc_lock);
614eabb5cdaSdim 				AGP_DPF("AGP_BIND_PAGE failed %d\n", error);
61540d7c10bSoga 				return (error);
6160139788eSmatthieu 			}
6170139788eSmatthieu 		}
6180139788eSmatthieu 		done += seg->ds_len;
6190139788eSmatthieu 	}
6200139788eSmatthieu 
6210139788eSmatthieu 	/*
6220139788eSmatthieu 	 * Flush the cpu cache since we are providing a new mapping
6230139788eSmatthieu 	 * for these pages.
6240139788eSmatthieu 	 */
6250139788eSmatthieu 	agp_flush_cache();
6260139788eSmatthieu 
6270139788eSmatthieu 	/*
6280139788eSmatthieu 	 * Make sure the chipset gets the new mappings.
6290139788eSmatthieu 	 */
6300139788eSmatthieu 	AGP_FLUSH_TLB(sc);
6310139788eSmatthieu 
6320139788eSmatthieu 	mem->am_offset = offset;
6330139788eSmatthieu 	mem->am_is_bound = 1;
6340139788eSmatthieu 
635f0c692fcSoga 	rw_exit_write(&sc->sc_lock);
6360139788eSmatthieu 
63740d7c10bSoga 	return (0);
6380139788eSmatthieu }
6390139788eSmatthieu 
6400139788eSmatthieu int
64140d7c10bSoga agp_generic_unbind_memory(struct agp_softc *sc, struct agp_memory *mem)
6420139788eSmatthieu {
6430139788eSmatthieu 	int i;
6440139788eSmatthieu 
645f0c692fcSoga 	rw_enter_write(&sc->sc_lock);
6460139788eSmatthieu 
6470139788eSmatthieu 	if (!mem->am_is_bound) {
6480139788eSmatthieu 		printf("AGP: memory is not bound\n");
649f0c692fcSoga 		rw_exit_write(&sc->sc_lock);
65040d7c10bSoga 		return (EINVAL);
6510139788eSmatthieu 	}
6520139788eSmatthieu 
6530139788eSmatthieu 
6540139788eSmatthieu 	/*
6550139788eSmatthieu 	 * Unbind the individual pages and flush the chipset's
6560139788eSmatthieu 	 * TLB. Unwire the pages so they can be swapped.
6570139788eSmatthieu 	 */
6580139788eSmatthieu 	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
6590139788eSmatthieu 		AGP_UNBIND_PAGE(sc, mem->am_offset + i);
6600139788eSmatthieu 
6610139788eSmatthieu 	agp_flush_cache();
6620139788eSmatthieu 	AGP_FLUSH_TLB(sc);
6630139788eSmatthieu 
6640139788eSmatthieu 	bus_dmamap_unload(sc->sc_dmat, mem->am_dmamap);
6650139788eSmatthieu 	bus_dmamem_unmap(sc->sc_dmat, mem->am_virtual, mem->am_size);
6660139788eSmatthieu 	bus_dmamem_free(sc->sc_dmat, mem->am_dmaseg, mem->am_nseg);
6670139788eSmatthieu 
66840d7c10bSoga 	free(mem->am_dmaseg, M_AGP);
6690139788eSmatthieu 
6700139788eSmatthieu 	mem->am_offset = 0;
6710139788eSmatthieu 	mem->am_is_bound = 0;
6720139788eSmatthieu 
673f0c692fcSoga 	rw_exit_write(&sc->sc_lock);
6740139788eSmatthieu 
67540d7c10bSoga 	return (0);
6760139788eSmatthieu }
6770139788eSmatthieu 
6780139788eSmatthieu int
6790139788eSmatthieu agp_alloc_dmamem(bus_dma_tag_t tag, size_t size, int flags,
6800139788eSmatthieu     bus_dmamap_t *mapp, caddr_t *vaddr, bus_addr_t *baddr,
6810139788eSmatthieu     bus_dma_segment_t *seg, int nseg, int *rseg)
6820139788eSmatthieu 
6830139788eSmatthieu {
6840139788eSmatthieu 	int error, level = 0;
6850139788eSmatthieu 
6860139788eSmatthieu 	if ((error = bus_dmamem_alloc(tag, size, PAGE_SIZE, 0,
6870139788eSmatthieu 	    seg, nseg, rseg, BUS_DMA_NOWAIT)) != 0)
6880139788eSmatthieu 		goto out;
6890139788eSmatthieu 	level++;
6900139788eSmatthieu 
6910139788eSmatthieu 	if ((error = bus_dmamem_map(tag, seg, *rseg, size, vaddr,
6920139788eSmatthieu 	    BUS_DMA_NOWAIT | flags)) != 0)
6930139788eSmatthieu 		goto out;
6940139788eSmatthieu 	level++;
6950139788eSmatthieu 
6960139788eSmatthieu 	if ((error = bus_dmamap_create(tag, size, *rseg, size, 0,
6970139788eSmatthieu 	    BUS_DMA_NOWAIT, mapp)) != 0)
6980139788eSmatthieu 		goto out;
6990139788eSmatthieu 	level++;
7000139788eSmatthieu 
7010139788eSmatthieu 	if ((error = bus_dmamap_load(tag, *mapp, *vaddr, size, NULL,
7020139788eSmatthieu 	    BUS_DMA_NOWAIT)) != 0)
7030139788eSmatthieu 		goto out;
7040139788eSmatthieu 
7050139788eSmatthieu 	*baddr = (*mapp)->dm_segs[0].ds_addr;
7060139788eSmatthieu 
70740d7c10bSoga 	return (0);
7080139788eSmatthieu out:
7090139788eSmatthieu 	switch (level) {
7100139788eSmatthieu 	case 3:
7110139788eSmatthieu 		bus_dmamap_destroy(tag, *mapp);
7120139788eSmatthieu 		/* FALLTHROUGH */
7130139788eSmatthieu 	case 2:
7140139788eSmatthieu 		bus_dmamem_unmap(tag, *vaddr, size);
7150139788eSmatthieu 		/* FALLTHROUGH */
7160139788eSmatthieu 	case 1:
7170139788eSmatthieu 		bus_dmamem_free(tag, seg, *rseg);
7180139788eSmatthieu 		break;
7190139788eSmatthieu 	default:
7200139788eSmatthieu 		break;
7210139788eSmatthieu 	}
7220139788eSmatthieu 
72340d7c10bSoga 	return (error);
7240139788eSmatthieu }
7250139788eSmatthieu 
7260139788eSmatthieu void
7270139788eSmatthieu agp_free_dmamem(bus_dma_tag_t tag, size_t size, bus_dmamap_t map,
7280139788eSmatthieu     caddr_t vaddr, bus_dma_segment_t *seg, int nseg)
7290139788eSmatthieu {
7300139788eSmatthieu 
7310139788eSmatthieu 	bus_dmamap_unload(tag, map);
7320139788eSmatthieu 	bus_dmamap_destroy(tag, map);
7330139788eSmatthieu 	bus_dmamem_unmap(tag, vaddr, size);
7340139788eSmatthieu 	bus_dmamem_free(tag, seg, nseg);
7350139788eSmatthieu }
73640d7c10bSoga 
73740d7c10bSoga /* Helper functions used in both user and kernel APIs */
73840d7c10bSoga 
73970456743Soga int
74040d7c10bSoga agp_acquire_helper(void *dev, enum agp_acquire_state state)
74140d7c10bSoga {
74240d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *)dev;
74340d7c10bSoga 
74440d7c10bSoga 	if (sc->sc_state != AGP_ACQUIRE_FREE)
74540d7c10bSoga 		return (EBUSY);
74640d7c10bSoga 	sc->sc_state = state;
74740d7c10bSoga 
74840d7c10bSoga 	return (0);
74940d7c10bSoga }
75040d7c10bSoga 
75170456743Soga int
75240d7c10bSoga agp_release_helper(void *dev, enum agp_acquire_state state)
75340d7c10bSoga {
75440d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *)dev;
75540d7c10bSoga 	struct agp_memory* mem;
75640d7c10bSoga 
75740d7c10bSoga 	if (sc->sc_state == AGP_ACQUIRE_FREE)
75840d7c10bSoga 		return (0);
75940d7c10bSoga 
76040d7c10bSoga 	if (sc->sc_state != state)
76140d7c10bSoga 		return (EBUSY);
76240d7c10bSoga 
76340d7c10bSoga 	/*
76440d7c10bSoga 	 * Clear out the aperture and free any
76540d7c10bSoga 	 * outstanding memory blocks.
76640d7c10bSoga 	 */
76740d7c10bSoga 	TAILQ_FOREACH(mem, &sc->sc_memory, am_link) {
76840d7c10bSoga 		if (mem->am_is_bound) {
76940d7c10bSoga 			printf("agp_release_helper: mem %d is bound\n",
77040d7c10bSoga 			    mem->am_id);
77140d7c10bSoga 			AGP_UNBIND_MEMORY(sc, mem);
77240d7c10bSoga 		}
77340d7c10bSoga 	}
77440d7c10bSoga 	sc->sc_state = AGP_ACQUIRE_FREE;
77540d7c10bSoga 	return (0);
77640d7c10bSoga }
77740d7c10bSoga 
77840d7c10bSoga /* Implementation of the userland ioctl API */
77940d7c10bSoga 
78070456743Soga int
78140d7c10bSoga agp_info_user(void *dev, agp_info *info)
78240d7c10bSoga {
78340d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
78440d7c10bSoga 
78540d7c10bSoga 	if (!sc->sc_chipc)
78640d7c10bSoga 		return (ENXIO);
78740d7c10bSoga 
78840d7c10bSoga 	bzero(info, sizeof *info);
78940d7c10bSoga 	info->bridge_id = sc->sc_id;
79040d7c10bSoga 	if (sc->sc_capoff != 0)
79140d7c10bSoga 		info->agp_mode = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
79240d7c10bSoga 		    AGP_STATUS + sc->sc_capoff);
79340d7c10bSoga 	else
79440d7c10bSoga 		info->agp_mode = 0; /* i810 doesn't have real AGP */
79540d7c10bSoga 	info->aper_base = sc->sc_apaddr;
79640d7c10bSoga 	info->aper_size = AGP_GET_APERTURE(sc) >> 20;
79740d7c10bSoga 	info->pg_total =
79840d7c10bSoga 	info->pg_system = sc->sc_maxmem >> AGP_PAGE_SHIFT;
79940d7c10bSoga 	info->pg_used = sc->sc_allocated >> AGP_PAGE_SHIFT;
80040d7c10bSoga 
80140d7c10bSoga 	return (0);
80240d7c10bSoga }
80340d7c10bSoga 
80470456743Soga int
80540d7c10bSoga agp_setup_user(void *dev, agp_setup *setup)
80640d7c10bSoga {
80740d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
808e47997a5Soga 
80940d7c10bSoga 	return (AGP_ENABLE(sc, setup->agp_mode));
81040d7c10bSoga }
81140d7c10bSoga 
81270456743Soga int
81340d7c10bSoga agp_allocate_user(void *dev, agp_allocate *alloc)
81440d7c10bSoga {
81540d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
81640d7c10bSoga 	struct agp_memory* mem;
81740d7c10bSoga 	size_t size = alloc->pg_count << AGP_PAGE_SHIFT;
81840d7c10bSoga 
81940d7c10bSoga 	if (sc->sc_allocated + size > sc->sc_maxmem)
82040d7c10bSoga 		return (EINVAL);
82140d7c10bSoga 
82240d7c10bSoga 	mem = AGP_ALLOC_MEMORY(sc, alloc->type, size);
82340d7c10bSoga 	if (mem) {
82440d7c10bSoga 		alloc->key = mem->am_id;
82540d7c10bSoga 		alloc->physical = mem->am_physical;
82640d7c10bSoga 		return (0);
82740d7c10bSoga 	} else
82840d7c10bSoga 		return (ENOMEM);
82940d7c10bSoga }
83040d7c10bSoga 
83170456743Soga int
83240d7c10bSoga agp_deallocate_user(void *dev, int id)
83340d7c10bSoga {
83440d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
83540d7c10bSoga 	struct agp_memory *mem = agp_find_memory(sc, id);
83640d7c10bSoga 	if (mem) {
83740d7c10bSoga 		AGP_FREE_MEMORY(sc, mem);
83840d7c10bSoga 		return (0);
83940d7c10bSoga 	} else
84040d7c10bSoga 		return (ENOENT);
84140d7c10bSoga }
84240d7c10bSoga 
84370456743Soga int
84440d7c10bSoga agp_bind_user(void *dev, agp_bind *bind)
84540d7c10bSoga {
84640d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
84740d7c10bSoga 	struct agp_memory *mem = agp_find_memory(sc, bind->key);
84840d7c10bSoga 
84940d7c10bSoga 	if (!mem)
85040d7c10bSoga 		return (ENOENT);
85140d7c10bSoga 
85240d7c10bSoga 	return (AGP_BIND_MEMORY(sc, mem, bind->pg_start << AGP_PAGE_SHIFT));
85340d7c10bSoga }
85440d7c10bSoga 
85540d7c10bSoga 
85670456743Soga int
85740d7c10bSoga agp_unbind_user(void *dev, agp_unbind *unbind)
85840d7c10bSoga {
85940d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
86040d7c10bSoga 	struct agp_memory *mem = agp_find_memory(sc, unbind->key);
86140d7c10bSoga 
86240d7c10bSoga 	if (!mem)
86340d7c10bSoga 		return (ENOENT);
86440d7c10bSoga 
86540d7c10bSoga 	return (AGP_UNBIND_MEMORY(sc, mem));
86640d7c10bSoga }
86740d7c10bSoga 
86840d7c10bSoga /* Implementation of the kernel api */
86940d7c10bSoga 
87040d7c10bSoga void *
87140d7c10bSoga agp_find_device(int unit)
87240d7c10bSoga {
873*8f8e4ea1Soga 	if (unit >= agp_cd.cd_ndevs || unit < 0)
874*8f8e4ea1Soga 		return (NULL);
875*8f8e4ea1Soga 	return (agp_cd.cd_devs[unit]);
87640d7c10bSoga }
87740d7c10bSoga 
87840d7c10bSoga enum agp_acquire_state
87940d7c10bSoga agp_state(void *dev)
88040d7c10bSoga {
88140d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
88240d7c10bSoga         return (sc->sc_state);
88340d7c10bSoga }
88440d7c10bSoga 
88540d7c10bSoga void
88640d7c10bSoga agp_get_info(void *dev, struct agp_info *info)
88740d7c10bSoga {
88840d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *)dev;
88940d7c10bSoga 
89040d7c10bSoga         info->ai_mode = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
89140d7c10bSoga 	    sc->sc_capoff + AGP_STATUS);
89240d7c10bSoga 	info->ai_aperture_base = sc->sc_apaddr;
89340d7c10bSoga 	info->ai_aperture_size = sc->sc_apsize;
89440d7c10bSoga         info->ai_memory_allowed = sc->sc_maxmem;
89540d7c10bSoga         info->ai_memory_used = sc->sc_allocated;
89640d7c10bSoga }
89740d7c10bSoga 
89840d7c10bSoga int
89940d7c10bSoga agp_acquire(void *dev)
90040d7c10bSoga {
90140d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *)dev;
902e47997a5Soga 
90340d7c10bSoga         return (agp_acquire_helper(sc, AGP_ACQUIRE_KERNEL));
90440d7c10bSoga }
90540d7c10bSoga 
90640d7c10bSoga int
90740d7c10bSoga agp_release(void *dev)
90840d7c10bSoga {
90940d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *)dev;
910e47997a5Soga 
91140d7c10bSoga         return (agp_release_helper(sc, AGP_ACQUIRE_KERNEL));
91240d7c10bSoga }
91340d7c10bSoga 
91440d7c10bSoga int
91540d7c10bSoga agp_enable(void *dev, u_int32_t mode)
91640d7c10bSoga {
91740d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
918e47997a5Soga 
91940d7c10bSoga         return (AGP_ENABLE(sc, mode));
92040d7c10bSoga }
92140d7c10bSoga 
922e47997a5Soga void *
923e47997a5Soga agp_alloc_memory(void *dev, int type, vsize_t bytes)
92440d7c10bSoga {
92540d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *)dev;
926e47997a5Soga 
92740d7c10bSoga         return  ((void *) AGP_ALLOC_MEMORY(sc, type, bytes));
92840d7c10bSoga }
92940d7c10bSoga 
930e47997a5Soga void
931e47997a5Soga agp_free_memory(void *dev, void *handle)
93240d7c10bSoga {
93340d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
93440d7c10bSoga         struct agp_memory *mem = (struct agp_memory *) handle;
935e47997a5Soga 
93640d7c10bSoga         AGP_FREE_MEMORY(sc, mem);
93740d7c10bSoga }
93840d7c10bSoga 
939e47997a5Soga int
940e47997a5Soga agp_bind_memory(void *dev, void *handle, off_t offset)
94140d7c10bSoga {
94240d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
94340d7c10bSoga 	struct agp_memory *mem = (struct agp_memory *) handle;
944e47997a5Soga 
94540d7c10bSoga 	return (AGP_BIND_MEMORY(sc, mem, offset));
94640d7c10bSoga }
94740d7c10bSoga 
948e47997a5Soga int
949e47997a5Soga agp_unbind_memory(void *dev, void *handle)
95040d7c10bSoga {
95140d7c10bSoga 	struct agp_softc *sc = (struct agp_softc *) dev;
95240d7c10bSoga         struct agp_memory *mem = (struct agp_memory *) handle;
953e47997a5Soga 
95440d7c10bSoga         return (AGP_UNBIND_MEMORY(sc, mem));
95540d7c10bSoga }
95640d7c10bSoga 
957e47997a5Soga void
958e47997a5Soga agp_memory_info(void *dev, void *handle, struct
95940d7c10bSoga     agp_memory_info *mi)
96040d7c10bSoga {
96140d7c10bSoga         struct agp_memory *mem = (struct agp_memory *) handle;
96240d7c10bSoga 
96340d7c10bSoga         mi->ami_size = mem->am_size;
96440d7c10bSoga         mi->ami_physical = mem->am_physical;
96540d7c10bSoga         mi->ami_offset = mem->am_offset;
96640d7c10bSoga         mi->ami_is_bound = mem->am_is_bound;
96740d7c10bSoga }
968