diff options
author | tijl <tijl@FreeBSD.org> | 2014-10-30 11:27:03 +0000 |
---|---|---|
committer | tijl <tijl@FreeBSD.org> | 2014-10-30 11:27:03 +0000 |
commit | 94a2b8b914e7c5f71084669d446c72fbd61523fd (patch) | |
tree | 11f7545a1c901c4ea8a2c50787393418278d20fa /sys/dev/agp | |
parent | 41aec014afb43830da6d08ba225ee49c7a8be48f (diff) | |
download | FreeBSD-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.c | 73 | ||||
-rw-r--r-- | sys/dev/agp/agpvar.h | 13 |
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) |