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;
}