summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoranholt <anholt@FreeBSD.org>2006-05-16 16:19:48 +0000
committeranholt <anholt@FreeBSD.org>2006-05-16 16:19:48 +0000
commit20cce22a72b37b52bfc4d68bd64496d133af8e1c (patch)
tree87bd22e50824fc9d2af219755fc4568a47dc8387
parentc8827595373ff15c22166e47bbeb9903eae6e07d (diff)
downloadFreeBSD-src-20cce22a72b37b52bfc4d68bd64496d133af8e1c.zip
FreeBSD-src-20cce22a72b37b52bfc4d68bd64496d133af8e1c.tar.gz
Add support for allocating one larger than page-sized contiguous block of memory
with a physical address. This is used for hardware ARGB cursor support on newer chipsets.
-rw-r--r--sys/dev/agp/agp_i810.c136
-rw-r--r--sys/pci/agp_i810.c136
2 files changed, 220 insertions, 52 deletions
diff --git a/sys/dev/agp/agp_i810.c b/sys/dev/agp/agp_i810.c
index 7b21a41..11cb650 100644
--- a/sys/dev/agp/agp_i810.c
+++ b/sys/dev/agp/agp_i810.c
@@ -88,6 +88,8 @@ struct agp_i810_softc {
struct resource *gtt; /* memory mapped GATT entries */
bus_space_tag_t gtt_bst; /* bus_space tag */
bus_space_handle_t gtt_bsh; /* bus_space handle */
+
+ void *argb_cursor; /* contigmalloc area for ARGB cursor */
};
static const char*
@@ -697,34 +699,53 @@ agp_i810_alloc_memory(device_t dev, int type, vm_size_t size)
return 0;
} else if (type == 2) {
/*
- * Bogus mapping of a single page for the hardware cursor.
+ * Type 2 is the contiguous physical memory type, that hands
+ * back a physical address. This is used for cursors on i810.
+ * Hand back as many single pages with physical as the user
+ * wants, but only allow one larger allocation (ARGB cursor)
+ * for simplicity.
*/
- if (size != AGP_PAGE_SIZE)
- return 0;
+ if (size != AGP_PAGE_SIZE) {
+ if (sc->argb_cursor != NULL)
+ return 0;
+
+ /* Allocate memory for ARGB cursor, if we can. */
+ sc->argb_cursor = contigmalloc(size, M_AGP,
+ 0, 0, ~0, PAGE_SIZE, 0);
+ if (sc->argb_cursor == NULL)
+ return 0;
+ }
}
mem = malloc(sizeof *mem, M_AGP, M_WAITOK);
mem->am_id = sc->agp.as_nextid++;
mem->am_size = size;
mem->am_type = type;
- if (type != 1)
+ if (type != 1 && (type != 2 || size == AGP_PAGE_SIZE))
mem->am_obj = vm_object_allocate(OBJT_DEFAULT,
atop(round_page(size)));
else
mem->am_obj = 0;
if (type == 2) {
- /*
- * Allocate and wire down the page now so that we can
- * get its physical address.
- */
- vm_page_t m;
-
- VM_OBJECT_LOCK(mem->am_obj);
- m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_NOBUSY |
- VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY);
- VM_OBJECT_UNLOCK(mem->am_obj);
- mem->am_physical = VM_PAGE_TO_PHYS(m);
+ if (size == AGP_PAGE_SIZE) {
+ /*
+ * Allocate and wire down the page now so that we can
+ * get its physical address.
+ */
+ vm_page_t m;
+
+ VM_OBJECT_LOCK(mem->am_obj);
+ m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_NOBUSY |
+ VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY);
+ VM_OBJECT_UNLOCK(mem->am_obj);
+ mem->am_physical = VM_PAGE_TO_PHYS(m);
+ } else {
+ /* Our allocation is already nicely wired down for us.
+ * Just grab the physical address.
+ */
+ mem->am_physical = vtophys(sc->argb_cursor);
+ }
} else {
mem->am_physical = 0;
}
@@ -746,17 +767,22 @@ agp_i810_free_memory(device_t dev, struct agp_memory *mem)
return EBUSY;
if (mem->am_type == 2) {
- /*
- * Unwire the page which we wired in alloc_memory.
- */
- vm_page_t m;
-
- VM_OBJECT_LOCK(mem->am_obj);
- m = vm_page_lookup(mem->am_obj, 0);
- VM_OBJECT_UNLOCK(mem->am_obj);
- vm_page_lock_queues();
- vm_page_unwire(m, 0);
- vm_page_unlock_queues();
+ if (mem->am_size == AGP_PAGE_SIZE) {
+ /*
+ * Unwire the page which we wired in alloc_memory.
+ */
+ vm_page_t m;
+
+ VM_OBJECT_LOCK(mem->am_obj);
+ m = vm_page_lookup(mem->am_obj, 0);
+ VM_OBJECT_UNLOCK(mem->am_obj);
+ vm_page_lock_queues();
+ vm_page_unwire(m, 0);
+ vm_page_unlock_queues();
+ } else {
+ contigfree(sc->argb_cursor, mem->am_size, M_AGP);
+ sc->argb_cursor = NULL;
+ }
}
sc->agp.as_allocated -= mem->am_size;
@@ -774,6 +800,40 @@ agp_i810_bind_memory(device_t dev, struct agp_memory *mem,
struct agp_i810_softc *sc = device_get_softc(dev);
vm_offset_t i;
+ /* Do some sanity checks first. */
+ if (offset < 0 || (offset & (AGP_PAGE_SIZE - 1)) != 0 ||
+ offset + mem->am_size > AGP_GET_APERTURE(dev)) {
+ device_printf(dev, "binding memory at bad offset %#x\n",
+ (int)offset);
+ return EINVAL;
+ }
+
+ if (mem->am_type == 2 && mem->am_size != AGP_PAGE_SIZE) {
+ mtx_lock(&sc->agp.as_lock);
+ if (mem->am_is_bound) {
+ mtx_unlock(&sc->agp.as_lock);
+ return EINVAL;
+ }
+ /* The memory's already wired down, just stick it in the GTT. */
+ for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
+ u_int32_t physical = mem->am_physical + i;
+
+ if (sc->chiptype == CHIP_I915) {
+ WRITEGTT(((offset + i) >> AGP_PAGE_SHIFT) * 4,
+ physical | 1);
+ } else {
+ WRITE4(AGP_I810_GTT +
+ ((offset + i) >> AGP_PAGE_SHIFT) * 4,
+ physical | 1);
+ }
+ }
+ agp_flush_cache();
+ mem->am_offset = offset;
+ mem->am_is_bound = 1;
+ mtx_unlock(&sc->agp.as_lock);
+ return 0;
+ }
+
if (mem->am_type != 1)
return agp_generic_bind_memory(dev, mem, offset);
@@ -794,6 +854,30 @@ agp_i810_unbind_memory(device_t dev, struct agp_memory *mem)
struct agp_i810_softc *sc = device_get_softc(dev);
vm_offset_t i;
+ if (mem->am_type == 2 && mem->am_size != AGP_PAGE_SIZE) {
+ mtx_lock(&sc->agp.as_lock);
+ if (!mem->am_is_bound) {
+ mtx_unlock(&sc->agp.as_lock);
+ return EINVAL;
+ }
+
+ for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
+ vm_offset_t offset = mem->am_offset;
+
+ if (sc->chiptype == CHIP_I915) {
+ WRITEGTT(((offset + i) >> AGP_PAGE_SHIFT) * 4,
+ 0);
+ } else {
+ WRITE4(AGP_I810_GTT +
+ ((offset + i) >> AGP_PAGE_SHIFT) * 4, 0);
+ }
+ }
+ agp_flush_cache();
+ mem->am_is_bound = 0;
+ mtx_unlock(&sc->agp.as_lock);
+ return 0;
+ }
+
if (mem->am_type != 1)
return agp_generic_unbind_memory(dev, mem);
diff --git a/sys/pci/agp_i810.c b/sys/pci/agp_i810.c
index 7b21a41..11cb650 100644
--- a/sys/pci/agp_i810.c
+++ b/sys/pci/agp_i810.c
@@ -88,6 +88,8 @@ struct agp_i810_softc {
struct resource *gtt; /* memory mapped GATT entries */
bus_space_tag_t gtt_bst; /* bus_space tag */
bus_space_handle_t gtt_bsh; /* bus_space handle */
+
+ void *argb_cursor; /* contigmalloc area for ARGB cursor */
};
static const char*
@@ -697,34 +699,53 @@ agp_i810_alloc_memory(device_t dev, int type, vm_size_t size)
return 0;
} else if (type == 2) {
/*
- * Bogus mapping of a single page for the hardware cursor.
+ * Type 2 is the contiguous physical memory type, that hands
+ * back a physical address. This is used for cursors on i810.
+ * Hand back as many single pages with physical as the user
+ * wants, but only allow one larger allocation (ARGB cursor)
+ * for simplicity.
*/
- if (size != AGP_PAGE_SIZE)
- return 0;
+ if (size != AGP_PAGE_SIZE) {
+ if (sc->argb_cursor != NULL)
+ return 0;
+
+ /* Allocate memory for ARGB cursor, if we can. */
+ sc->argb_cursor = contigmalloc(size, M_AGP,
+ 0, 0, ~0, PAGE_SIZE, 0);
+ if (sc->argb_cursor == NULL)
+ return 0;
+ }
}
mem = malloc(sizeof *mem, M_AGP, M_WAITOK);
mem->am_id = sc->agp.as_nextid++;
mem->am_size = size;
mem->am_type = type;
- if (type != 1)
+ if (type != 1 && (type != 2 || size == AGP_PAGE_SIZE))
mem->am_obj = vm_object_allocate(OBJT_DEFAULT,
atop(round_page(size)));
else
mem->am_obj = 0;
if (type == 2) {
- /*
- * Allocate and wire down the page now so that we can
- * get its physical address.
- */
- vm_page_t m;
-
- VM_OBJECT_LOCK(mem->am_obj);
- m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_NOBUSY |
- VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY);
- VM_OBJECT_UNLOCK(mem->am_obj);
- mem->am_physical = VM_PAGE_TO_PHYS(m);
+ if (size == AGP_PAGE_SIZE) {
+ /*
+ * Allocate and wire down the page now so that we can
+ * get its physical address.
+ */
+ vm_page_t m;
+
+ VM_OBJECT_LOCK(mem->am_obj);
+ m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_NOBUSY |
+ VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY);
+ VM_OBJECT_UNLOCK(mem->am_obj);
+ mem->am_physical = VM_PAGE_TO_PHYS(m);
+ } else {
+ /* Our allocation is already nicely wired down for us.
+ * Just grab the physical address.
+ */
+ mem->am_physical = vtophys(sc->argb_cursor);
+ }
} else {
mem->am_physical = 0;
}
@@ -746,17 +767,22 @@ agp_i810_free_memory(device_t dev, struct agp_memory *mem)
return EBUSY;
if (mem->am_type == 2) {
- /*
- * Unwire the page which we wired in alloc_memory.
- */
- vm_page_t m;
-
- VM_OBJECT_LOCK(mem->am_obj);
- m = vm_page_lookup(mem->am_obj, 0);
- VM_OBJECT_UNLOCK(mem->am_obj);
- vm_page_lock_queues();
- vm_page_unwire(m, 0);
- vm_page_unlock_queues();
+ if (mem->am_size == AGP_PAGE_SIZE) {
+ /*
+ * Unwire the page which we wired in alloc_memory.
+ */
+ vm_page_t m;
+
+ VM_OBJECT_LOCK(mem->am_obj);
+ m = vm_page_lookup(mem->am_obj, 0);
+ VM_OBJECT_UNLOCK(mem->am_obj);
+ vm_page_lock_queues();
+ vm_page_unwire(m, 0);
+ vm_page_unlock_queues();
+ } else {
+ contigfree(sc->argb_cursor, mem->am_size, M_AGP);
+ sc->argb_cursor = NULL;
+ }
}
sc->agp.as_allocated -= mem->am_size;
@@ -774,6 +800,40 @@ agp_i810_bind_memory(device_t dev, struct agp_memory *mem,
struct agp_i810_softc *sc = device_get_softc(dev);
vm_offset_t i;
+ /* Do some sanity checks first. */
+ if (offset < 0 || (offset & (AGP_PAGE_SIZE - 1)) != 0 ||
+ offset + mem->am_size > AGP_GET_APERTURE(dev)) {
+ device_printf(dev, "binding memory at bad offset %#x\n",
+ (int)offset);
+ return EINVAL;
+ }
+
+ if (mem->am_type == 2 && mem->am_size != AGP_PAGE_SIZE) {
+ mtx_lock(&sc->agp.as_lock);
+ if (mem->am_is_bound) {
+ mtx_unlock(&sc->agp.as_lock);
+ return EINVAL;
+ }
+ /* The memory's already wired down, just stick it in the GTT. */
+ for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
+ u_int32_t physical = mem->am_physical + i;
+
+ if (sc->chiptype == CHIP_I915) {
+ WRITEGTT(((offset + i) >> AGP_PAGE_SHIFT) * 4,
+ physical | 1);
+ } else {
+ WRITE4(AGP_I810_GTT +
+ ((offset + i) >> AGP_PAGE_SHIFT) * 4,
+ physical | 1);
+ }
+ }
+ agp_flush_cache();
+ mem->am_offset = offset;
+ mem->am_is_bound = 1;
+ mtx_unlock(&sc->agp.as_lock);
+ return 0;
+ }
+
if (mem->am_type != 1)
return agp_generic_bind_memory(dev, mem, offset);
@@ -794,6 +854,30 @@ agp_i810_unbind_memory(device_t dev, struct agp_memory *mem)
struct agp_i810_softc *sc = device_get_softc(dev);
vm_offset_t i;
+ if (mem->am_type == 2 && mem->am_size != AGP_PAGE_SIZE) {
+ mtx_lock(&sc->agp.as_lock);
+ if (!mem->am_is_bound) {
+ mtx_unlock(&sc->agp.as_lock);
+ return EINVAL;
+ }
+
+ for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) {
+ vm_offset_t offset = mem->am_offset;
+
+ if (sc->chiptype == CHIP_I915) {
+ WRITEGTT(((offset + i) >> AGP_PAGE_SHIFT) * 4,
+ 0);
+ } else {
+ WRITE4(AGP_I810_GTT +
+ ((offset + i) >> AGP_PAGE_SHIFT) * 4, 0);
+ }
+ }
+ agp_flush_cache();
+ mem->am_is_bound = 0;
+ mtx_unlock(&sc->agp.as_lock);
+ return 0;
+ }
+
if (mem->am_type != 1)
return agp_generic_unbind_memory(dev, mem);
OpenPOWER on IntegriCloud