Module Name:    src
Committed By:   riastradh
Date:           Tue Jun 10 22:02:58 UTC 2014

Modified Files:
        src/sys/dev/pci: agp_i810.c

Log Message:
Rework agp_i810 memory binding.

Principal reason is that the GTT size is not necessarily the same as
the aperture size: the GPU may have a bigger virtual address space
than the CPU can see through the aperture.

While here, factor the code a little more legibly and name some magic
constants.


To generate a diff of this commit:
cvs rdiff -u -r1.85 -r1.86 src/sys/dev/pci/agp_i810.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/pci/agp_i810.c
diff -u src/sys/dev/pci/agp_i810.c:1.85 src/sys/dev/pci/agp_i810.c:1.86
--- src/sys/dev/pci/agp_i810.c:1.85	Tue Jun 10 14:00:56 2014
+++ src/sys/dev/pci/agp_i810.c	Tue Jun 10 22:02:58 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: agp_i810.c,v 1.85 2014/06/10 14:00:56 riastradh Exp $	*/
+/*	$NetBSD: agp_i810.c,v 1.86 2014/06/10 22:02:58 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2000 Doug Rabson
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: agp_i810.c,v 1.85 2014/06/10 14:00:56 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: agp_i810.c,v 1.86 2014/06/10 22:02:58 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -81,7 +81,14 @@ static int agp_i810_enable(struct agp_so
 static struct agp_memory *agp_i810_alloc_memory(struct agp_softc *, int,
 						vsize_t);
 static int agp_i810_free_memory(struct agp_softc *, struct agp_memory *);
-static int agp_i810_bind_memory(struct agp_softc *, struct agp_memory *, off_t);
+static int agp_i810_bind_memory(struct agp_softc *, struct agp_memory *,
+		off_t);
+static int agp_i810_bind_memory_main(struct agp_softc *, struct agp_memory *,
+		off_t);
+static int agp_i810_bind_memory_dcache(struct agp_softc *, struct agp_memory *,
+		off_t);
+static int agp_i810_bind_memory_hwcursor(struct agp_softc *,
+		struct agp_memory *, off_t);
 static int agp_i810_unbind_memory(struct agp_softc *, struct agp_memory *);
 
 static bool agp_i810_resume(device_t, const pmf_qual_t *);
@@ -1033,73 +1040,11 @@ agp_i810_get_aperture(struct agp_softc *
 }
 
 static int
-agp_i810_set_aperture(struct agp_softc *sc, u_int32_t aperture)
+agp_i810_set_aperture(struct agp_softc *sc __unused,
+    uint32_t aperture __unused)
 {
-	struct agp_i810_softc *isc = sc->as_chipc;
-	pcireg_t reg;
-	u_int16_t miscc, gcc1;
-
-	switch (isc->chiptype) {
-	case CHIP_I810:
-		/*
-		 * Double check for sanity.
-		 */
-		if (aperture != (32 * 1024 * 1024) &&
-		    aperture != (64 * 1024 * 1024)) {
-			aprint_error_dev(sc->as_dev, "bad aperture size %d\n",
-			    aperture);
-			return EINVAL;
-		}
-
-		reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I810_SMRAM);
-		miscc = (u_int16_t)(reg >> 16);
-		miscc &= ~AGP_I810_MISCC_WINSIZE;
-		if (aperture == 32 * 1024 * 1024)
-			miscc |= AGP_I810_MISCC_WINSIZE_32;
-		else
-			miscc |= AGP_I810_MISCC_WINSIZE_64;
 
-		reg &= 0x0000ffff;
-		reg |= ((pcireg_t)miscc) << 16;
-		pci_conf_write(sc->as_pc, sc->as_tag, AGP_I810_SMRAM, reg);
-		break;
-	case CHIP_I830:
-		if (aperture != (64 * 1024 * 1024) &&
-		    aperture != (128 * 1024 * 1024)) {
-			aprint_error_dev(sc->as_dev, "bad aperture size %d\n",
-			    aperture);
-			return EINVAL;
-		}
-		reg = pci_conf_read(sc->as_pc, sc->as_tag, AGP_I830_GCC0);
-		gcc1 = (u_int16_t)(reg >> 16);
-		gcc1 &= ~AGP_I830_GCC1_GMASIZE;
-		if (aperture == 64 * 1024 * 1024)
-			gcc1 |= AGP_I830_GCC1_GMASIZE_64;
-		else
-			gcc1 |= AGP_I830_GCC1_GMASIZE_128;
-
-		reg &= 0x0000ffff;
-		reg |= ((pcireg_t)gcc1) << 16;
-		pci_conf_write(sc->as_pc, sc->as_tag, AGP_I830_GCC0, reg);
-		break;
-	case CHIP_I855:
-	case CHIP_I915:
-		if (aperture != agp_i810_get_aperture(sc)) {
-			aprint_error_dev(sc->as_dev, "bad aperture size %d\n",
-			    aperture);
-			return EINVAL;
-		}
-		break;
-	case CHIP_I965:
-		if (aperture != 512 * 1024 * 1024) {
-			aprint_error_dev(sc->as_dev, "bad aperture size %d\n",
-			    aperture);
-			return EINVAL;
-		}
-		break;
-	}
-
-	return 0;
+	return ENOSYS;
 }
 
 static int
@@ -1165,105 +1110,123 @@ agp_i810_enable(struct agp_softc *sc, u_
 	return 0;
 }
 
+#define	AGP_I810_MEMTYPE_MAIN		0
+#define	AGP_I810_MEMTYPE_DCACHE		1
+#define	AGP_I810_MEMTYPE_HWCURSOR	2
+
 static struct agp_memory *
 agp_i810_alloc_memory(struct agp_softc *sc, int type, vsize_t size)
 {
 	struct agp_i810_softc *isc = sc->as_chipc;
 	struct agp_memory *mem;
+	int error;
 
 #ifdef AGP_DEBUG
 	printf("AGP: alloc(%d, 0x%x)\n", type, (int) size);
 #endif
 
+	if (size <= 0)
+		return NULL;
 	if ((size & (AGP_PAGE_SIZE - 1)) != 0)
-		return 0;
-
+		return NULL;
 	if (sc->as_allocated + size > sc->as_maxmem)
-		return 0;
-
-	if (type == 1) {
-		/*
-		 * Mapping local DRAM into GATT.
-		 */
-		if (isc->chiptype != CHIP_I810 )
-			return 0;
+		return NULL;
+	switch (type) {
+	case AGP_I810_MEMTYPE_MAIN:
+		break;
+	case AGP_I810_MEMTYPE_DCACHE:
+		if (isc->chiptype != CHIP_I810)
+			return NULL;
 		if (size != isc->dcache_size)
-			return 0;
-	} else if (type == 2) {
-		/*
-		 * Bogus mapping for the hardware cursor.
-		 */
-		if (size != AGP_PAGE_SIZE && size != 4 * AGP_PAGE_SIZE)
-			return 0;
+			return NULL;
+		break;
+	case AGP_I810_MEMTYPE_HWCURSOR:
+		if ((size != AGP_PAGE_SIZE) &&
+		    (size != AGP_PAGE_SIZE*4))
+			return NULL;
+		break;
+	default:
+		return NULL;
 	}
 
-	mem = malloc(sizeof *mem, M_AGP, M_WAITOK|M_ZERO);
+	mem = malloc(sizeof(*mem), M_AGP, M_WAITOK|M_ZERO);
 	if (mem == NULL)
-		return NULL;
+		goto fail0;
 	mem->am_id = sc->as_nextid++;
 	mem->am_size = size;
 	mem->am_type = type;
 
-	if (type == 2) {
-		/*
-		 * Allocate and wire down the memory now so that we can
-		 * get its physical address.
-		 */
-		mem->am_dmaseg = malloc(sizeof *mem->am_dmaseg, M_AGP,
+	switch (type) {
+	case AGP_I810_MEMTYPE_MAIN:
+		error = bus_dmamap_create(sc->as_dmat, size,
+		    (size >> AGP_PAGE_SHIFT) + 1, size, 0, BUS_DMA_WAITOK,
+		    &mem->am_dmamap);
+		if (error)
+			goto fail1;
+		break;
+	case AGP_I810_MEMTYPE_DCACHE:
+		break;
+	case AGP_I810_MEMTYPE_HWCURSOR:
+		mem->am_dmaseg = malloc(sizeof(*mem->am_dmaseg), M_AGP,
 		    M_WAITOK);
-		if (mem->am_dmaseg == NULL) {
-			free(mem, M_AGP);
-			return NULL;
-		}
-		if (agp_alloc_dmamem(sc->as_dmat, size, 0,
-		    &mem->am_dmamap, &mem->am_virtual, &mem->am_physical,
-		    mem->am_dmaseg, 1, &mem->am_nseg) != 0) {
+		error = agp_alloc_dmamem(sc->as_dmat, size, 0, &mem->am_dmamap,
+		    &mem->am_virtual, &mem->am_physical, mem->am_dmaseg, 1,
+		    &mem->am_nseg);
+		if (error) {
 			free(mem->am_dmaseg, M_AGP);
-			free(mem, M_AGP);
-			return NULL;
-		}
-		memset(mem->am_virtual, 0, size);
-	} else if (type != 1) {
-		if (bus_dmamap_create(sc->as_dmat, size, size / PAGE_SIZE + 1,
-				      size, 0, BUS_DMA_NOWAIT,
-				      &mem->am_dmamap) != 0) {
-			free(mem, M_AGP);
-			return NULL;
+			goto fail1;
 		}
+		(void)memset(mem->am_virtual, 0, size);
+		break;
+	default:
+		panic("invalid agp memory type: %d", type);
 	}
 
 	TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link);
 	sc->as_allocated += size;
 
 	return mem;
+
+fail1:	free(mem, M_AGP);
+fail0:	return NULL;
 }
 
 static int
 agp_i810_free_memory(struct agp_softc *sc, struct agp_memory *mem)
 {
+
 	if (mem->am_is_bound)
 		return EBUSY;
 
-	if (mem->am_type == 2) {
+	switch (mem->am_type) {
+	case AGP_I810_MEMTYPE_MAIN:
+	case AGP_I810_MEMTYPE_DCACHE:
+		break;
+	case AGP_I810_MEMTYPE_HWCURSOR:
 		agp_free_dmamem(sc->as_dmat, mem->am_size, mem->am_dmamap,
 		    mem->am_virtual, mem->am_dmaseg, mem->am_nseg);
 		free(mem->am_dmaseg, M_AGP);
+		break;
+	default:
+		panic("invalid agp i810 memory type: %d", mem->am_type);
 	}
 
 	sc->as_allocated -= mem->am_size;
 	TAILQ_REMOVE(&sc->as_memory, mem, am_link);
 	free(mem, M_AGP);
+
 	return 0;
 }
 
 static int
 agp_i810_bind_memory(struct agp_softc *sc, struct agp_memory *mem,
-		     off_t offset)
+    off_t offset)
 {
 	struct agp_i810_softc *isc = sc->as_chipc;
-	u_int32_t regval, i;
+	uint32_t pgtblctl;
+	int error;
 
-	if (mem->am_is_bound != 0)
+	if (mem->am_is_bound)
 		return EINVAL;
 
 	/*
@@ -1272,33 +1235,173 @@ agp_i810_bind_memory(struct agp_softc *s
 	 * to the GTT through the MMIO window.
 	 * Until the issue is solved, simply restore it.
 	 */
-	regval = bus_space_read_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL);
-	if (regval != isc->pgtblctl) {
-		printf("agp_i810_bind_memory: PGTBL_CTL is 0x%x - fixing\n",
-		       regval);
+	pgtblctl = bus_space_read_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL);
+	if (pgtblctl != isc->pgtblctl) {
+		printf("agp_i810_bind_memory: PGTBL_CTL is 0x%"PRIx32
+		    " - fixing\n", pgtblctl);
 		bus_space_write_4(isc->bst, isc->bsh, AGP_I810_PGTBL_CTL,
 		    isc->pgtblctl);
 	}
 
-	if (mem->am_type == 2) {
-		for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
-			agp_i810_bind_page(sc, offset + i,
-			    mem->am_physical + i);
-		mem->am_offset = offset;
-		mem->am_is_bound = 1;
-		return 0;
+	switch (mem->am_type) {
+	case AGP_I810_MEMTYPE_MAIN:
+		error = agp_i810_bind_memory_main(sc, mem, offset);
+		break;
+	case AGP_I810_MEMTYPE_DCACHE:
+		error = agp_i810_bind_memory_dcache(sc, mem, offset);
+		break;
+	case AGP_I810_MEMTYPE_HWCURSOR:
+		error = agp_i810_bind_memory_hwcursor(sc, mem, offset);
+		break;
+	default:
+		panic("invalid agp i810 memory type: %d", mem->am_type);
 	}
+	if (error)
+		return error;
 
-	if (mem->am_type != 1)
-		return agp_generic_bind_memory(sc, mem, offset);
+	/* Success!  */
+	mem->am_is_bound = 1;
+	return 0;
+}
+
+static int
+agp_i810_bind_memory_main(struct agp_softc *sc, struct agp_memory *mem,
+    off_t offset)
+{
+	struct agp_i810_softc *const isc = sc->as_chipc;
+	bus_dma_segment_t *segs;
+	int nseg_alloc, nseg;
+	uint32_t i, j;
+	unsigned seg;
+	bus_addr_t addr;
+	bus_size_t len;
+	int error;
 
-	if (isc->chiptype != CHIP_I810)
+	/* Ensure we have a sane size/offset that will fit.  */
+	if (offset < 0)
+		return EINVAL;
+	if (offset & (AGP_PAGE_SIZE - 1))
+		return EINVAL;
+	if (mem->am_size > ((isc->gtt_size/4) << AGP_PAGE_SHIFT))
+		return EINVAL;
+	if (offset > (((isc->gtt_size/4) << AGP_PAGE_SHIFT) -
+		mem->am_size))
 		return EINVAL;
 
-	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
-		agp_i810_write_gtt_entry(isc, i, i | 3);
-	mem->am_is_bound = 1;
+	/* Allocate an array of DMA segments.  */
+	nseg_alloc = (mem->am_size >> AGP_PAGE_SHIFT);
+	if (nseg_alloc > (SIZE_MAX / sizeof(*segs))) {
+		error = ENOMEM;
+		goto fail0;
+	}
+	segs = malloc(nseg_alloc * sizeof(*segs), M_AGP, M_WAITOK);
+
+	/* Allocate DMA-safe physical segments.  */
+	error = bus_dmamem_alloc(sc->as_dmat, mem->am_size, PAGE_SIZE,
+	    0, segs, nseg_alloc, &nseg, BUS_DMA_WAITOK);
+	if (error)
+		goto fail1;
+	KASSERT(nseg <= nseg_alloc);
+
+	/* Shrink the array of DMA segments if we can.  */
+	if (nseg < nseg_alloc) {
+		segs = realloc(segs, nseg, M_AGP, M_WAITOK);
+		nseg_alloc = nseg;
+	}
+
+	/* Load the DMA map.  */
+	error = bus_dmamap_load_raw(sc->as_dmat, mem->am_dmamap,
+	    segs, mem->am_nseg, mem->am_size, BUS_DMA_WAITOK);
+	if (error)
+		goto fail2;
+
+	/* Bind the pages in the GTT.  */
+	i = 0;
+	KASSERT((mem->am_size & (AGP_PAGE_SIZE - 1)) == 0);
+	for (seg = 0; seg < mem->am_dmamap->dm_nsegs; seg++) {
+		KASSERT((offset + i) < mem->am_size);
+		addr = mem->am_dmamap->dm_segs[seg].ds_addr;
+		len = MIN(mem->am_dmamap->dm_segs[seg].ds_len,
+		    (mem->am_size - (offset + i)));
+		do {
+			KASSERT(0 < len);
+			KASSERT((len & (AGP_PAGE_SIZE - 1)) == 0);
+			KASSERT((offset + i) < (mem->am_size - len));
+			error = agp_i810_bind_page(sc, offset + i, addr);
+			if (error)
+				goto fail3;
+			i += AGP_PAGE_SIZE;
+			addr += AGP_PAGE_SIZE;
+			len -= AGP_PAGE_SIZE;
+		} while (0 < len);
+	}
+
+	/* Success!  */
+	mem->am_dmaseg = segs;
+	mem->am_offset = offset;
+	return 0;
+
+fail3:	for (j = 0; j < i; j += AGP_PAGE_SIZE)
+		(void)agp_i810_unbind_page(sc, offset + j);
+	bus_dmamap_unload(sc->as_dmat, mem->am_dmamap);
+fail2:	bus_dmamem_free(sc->as_dmat, segs, nseg_alloc);
+fail1:	free(segs, M_AGP);
+fail0:	KASSERT(error);
+	return error;
+}
+
+#define	I810_GTT_PTE_VALID	0x01
+#define	I810_GTT_PTE_DCACHE	0x02
+
+static int
+agp_i810_bind_memory_dcache(struct agp_softc *sc, struct agp_memory *mem,
+    off_t offset)
+{
+	struct agp_i810_softc *const isc __diagused = sc->as_chipc;
+	uint32_t i, j;
+	int error;
+
+	KASSERT(isc->chiptype == CHIP_I810);
+
+	KASSERT((mem->am_size & (AGP_PAGE_SIZE - 1)) == 0);
+	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
+		/* XXX No offset?  */
+		error = agp_i810_write_gtt_entry(isc, i,
+		    i | I810_GTT_PTE_VALID | I810_GTT_PTE_DCACHE);
+		if (error)
+			goto fail0;
+	}
+
+	/* Success!  */
 	return 0;
+
+fail0:	for (j = 0; j < i; j += AGP_PAGE_SIZE)
+		(void)agp_i810_unbind_page(sc, offset + j);
+	return error;
+}
+
+static int
+agp_i810_bind_memory_hwcursor(struct agp_softc *sc, struct agp_memory *mem,
+    off_t offset)
+{
+	const bus_addr_t pa = mem->am_physical;
+	uint32_t i, j;
+	int error;
+
+	KASSERT((mem->am_size & (AGP_PAGE_SIZE - 1)) == 0);
+	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
+		error = agp_i810_bind_page(sc, offset + i, pa + i);
+		if (error)
+			goto fail0;
+	}
+
+	/* Success!  */
+	mem->am_offset = offset;
+	return 0;
+
+fail0:	for (j = 0; j < i; j += AGP_PAGE_SIZE)
+		(void)agp_i810_unbind_page(sc, offset + j);
+	return error;
 }
 
 static int
@@ -1307,25 +1410,24 @@ agp_i810_unbind_memory(struct agp_softc 
 	struct agp_i810_softc *isc = sc->as_chipc;
 	u_int32_t i;
 
-	if (mem->am_is_bound == 0)
+	if (!mem->am_is_bound)
 		return EINVAL;
 
-	if (mem->am_type == 2) {
+	switch (mem->am_type) {
+	case AGP_I810_MEMTYPE_MAIN:
+	case AGP_I810_MEMTYPE_HWCURSOR:
 		for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
 			agp_i810_unbind_page(sc, mem->am_offset + i);
-		mem->am_offset = 0;
-		mem->am_is_bound = 0;
-		return 0;
+		break;
+	case AGP_I810_MEMTYPE_DCACHE:
+		KASSERT(isc->chiptype == CHIP_I810);
+		for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
+			(void)agp_i810_write_gtt_entry(isc, i, 0);
+		break;
+	default:
+		panic("invalid agp i810 memory type: %d", mem->am_type);
 	}
 
-	if (mem->am_type != 1)
-		return agp_generic_unbind_memory(sc, mem);
-
-	if (isc->chiptype != CHIP_I810)
-		return EINVAL;
-
-	for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE)
-		agp_i810_write_gtt_entry(isc, i, 0);
 	mem->am_is_bound = 0;
 	return 0;
 }

Reply via email to