summaryrefslogtreecommitdiffstats
path: root/sys/dev/agp
diff options
context:
space:
mode:
authortijl <tijl@FreeBSD.org>2014-10-30 11:27:03 +0000
committertijl <tijl@FreeBSD.org>2014-10-30 11:27:03 +0000
commit94a2b8b914e7c5f71084669d446c72fbd61523fd (patch)
tree11f7545a1c901c4ea8a2c50787393418278d20fa /sys/dev/agp
parent41aec014afb43830da6d08ba225ee49c7a8be48f (diff)
downloadFreeBSD-src-94a2b8b914e7c5f71084669d446c72fbd61523fd.zip
FreeBSD-src-94a2b8b914e7c5f71084669d446c72fbd61523fd.tar.gz
Add two new functions to the AGP driver KPI to bind/unbind arbitrary sets
of pages into the GTT. Reviewed by: kib MFC after: 1 month
Diffstat (limited to 'sys/dev/agp')
-rw-r--r--sys/dev/agp/agp.c73
-rw-r--r--sys/dev/agp/agpvar.h13
2 files changed, 86 insertions, 0 deletions
diff --git a/sys/dev/agp/agp.c b/sys/dev/agp/agp.c
index 9eed774..37dd486 100644
--- a/sys/dev/agp/agp.c
+++ b/sys/dev/agp/agp.c
@@ -996,3 +996,76 @@ void agp_memory_info(device_t dev, void *handle, struct
mi->ami_offset = mem->am_offset;
mi->ami_is_bound = mem->am_is_bound;
}
+
+int
+agp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size,
+ vm_offset_t offset)
+{
+ struct agp_softc *sc;
+ vm_offset_t i, j, k, pa;
+ vm_page_t m;
+ int error;
+
+ if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
+ (offset & (AGP_PAGE_SIZE - 1)) != 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->as_lock);
+ for (i = 0; i < size; i += PAGE_SIZE) {
+ m = pages[OFF_TO_IDX(i)];
+
+ /*
+ * Install entries in the GATT, making sure that if
+ * AGP_PAGE_SIZE < PAGE_SIZE and size is not
+ * aligned to PAGE_SIZE, we don't modify too many GATT
+ * entries.
+ */
+ for (j = 0; j < PAGE_SIZE && i + j < size; j += AGP_PAGE_SIZE) {
+ pa = VM_PAGE_TO_PHYS(m) + j;
+ AGP_DPF("binding offset %#jx to pa %#jx\n",
+ (uintmax_t)offset + i + j, (uintmax_t)pa);
+ error = AGP_BIND_PAGE(dev, offset + i + j, pa);
+ if (error) {
+ /*
+ * Bail out. Reverse all the mappings.
+ */
+ for (k = 0; k < i + j; k += AGP_PAGE_SIZE)
+ AGP_UNBIND_PAGE(dev, offset + k);
+
+ mtx_unlock(&sc->as_lock);
+ return (error);
+ }
+ }
+ }
+
+ agp_flush_cache();
+ AGP_FLUSH_TLB(dev);
+
+ mtx_unlock(&sc->as_lock);
+ return (0);
+}
+
+int
+agp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset)
+{
+ struct agp_softc *sc;
+ vm_offset_t i;
+
+ if ((size & (AGP_PAGE_SIZE - 1)) != 0 ||
+ (offset & (AGP_PAGE_SIZE - 1)) != 0)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->as_lock);
+ for (i = 0; i < size; i += AGP_PAGE_SIZE)
+ AGP_UNBIND_PAGE(dev, offset + i);
+
+ agp_flush_cache();
+ AGP_FLUSH_TLB(dev);
+
+ mtx_unlock(&sc->as_lock);
+ return (0);
+}
diff --git a/sys/dev/agp/agpvar.h b/sys/dev/agp/agpvar.h
index 5aeebc9..2c2c87a 100644
--- a/sys/dev/agp/agpvar.h
+++ b/sys/dev/agp/agpvar.h
@@ -122,6 +122,19 @@ int agp_unbind_memory(device_t dev, void *handle);
*/
void agp_memory_info(device_t dev, void *handle, struct agp_memory_info *mi);
+/*
+ * Bind a set of pages at a given offset within the AGP aperture.
+ * Returns EINVAL if the given size or offset is not at an AGP page boundary.
+ */
+int agp_bind_pages(device_t dev, vm_page_t *pages, vm_size_t size,
+ vm_offset_t offset);
+
+/*
+ * Unbind a set of pages from the AGP aperture.
+ * Returns EINVAL if the given size or offset is not at an AGP page boundary.
+ */
+int agp_unbind_pages(device_t dev, vm_size_t size, vm_offset_t offset);
+
#define AGP_NORMAL_MEMORY 0
#define AGP_USER_TYPES (1 << 16)
OpenPOWER on IntegriCloud