[AGPGART] Allow drm-populated agp memory types

This patch allows drm to populate an agpgart structure with pages of its own.
It's needed for the new drm memory manager which dynamically flips pages in and out of AGP.

The patch modifies the generic functions as well as the intel agp driver. The intel drm driver is
currently the only one supporting the new memory manager.

Other agp drivers may need some minor fixing up once they have a corresponding memory manager enabled drm driver.

AGP memory types >= AGP_USER_TYPES are not populated by the agpgart driver, but the drm is expected
to do that, as well as taking care of cache- and tlb flushing when needed.

It's not possible to request these types from user space using agpgart ioctls.

The Intel driver also gets a new memory type for pages that can be bound cached to the intel GTT.

Signed-off-by: Thomas Hellstrom <thomas@tungstengraphics.com>
Signed-off-by: Dave Jones <davej@redhat.com>
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 3491d6f..a627b77 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -101,6 +101,67 @@
 	return -1;
 }
 
+/*
+ * Use kmalloc if possible for the page list. Otherwise fall back to
+ * vmalloc. This speeds things up and also saves memory for small AGP
+ * regions.
+ */
+
+void agp_alloc_page_array(size_t size, struct agp_memory *mem)
+{
+	mem->memory = NULL;
+	mem->vmalloc_flag = 0;
+
+	if (size <= 2*PAGE_SIZE) {
+		mem->memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY);
+	}
+	if (mem->memory == NULL) {
+		mem->memory = vmalloc(size);
+		mem->vmalloc_flag = 1;
+	}
+}
+EXPORT_SYMBOL(agp_alloc_page_array);
+
+void agp_free_page_array(struct agp_memory *mem)
+{
+	if (mem->vmalloc_flag) {
+		vfree(mem->memory);
+	} else {
+		kfree(mem->memory);
+	}
+}
+EXPORT_SYMBOL(agp_free_page_array);
+
+
+static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages)
+{
+	struct agp_memory *new;
+	unsigned long alloc_size = num_agp_pages*sizeof(struct page *);
+
+	new = kmalloc(sizeof(struct agp_memory), GFP_KERNEL);
+
+	if (new == NULL)
+		return NULL;
+
+	memset(new, 0, sizeof(struct agp_memory));
+	new->key = agp_get_key();
+
+	if (new->key < 0) {
+		kfree(new);
+		return NULL;
+	}
+
+	agp_alloc_page_array(alloc_size, new);
+
+	if (new->memory == NULL) {
+		agp_free_key(new->key);
+		kfree(new);
+		return NULL;
+	}
+	new->num_scratch_pages = 0;
+	return new;
+}
+
 
 struct agp_memory *agp_create_memory(int scratch_pages)
 {
@@ -116,7 +177,8 @@
 		kfree(new);
 		return NULL;
 	}
-	new->memory = vmalloc(PAGE_SIZE * scratch_pages);
+
+	agp_alloc_page_array(PAGE_SIZE * scratch_pages, new);
 
 	if (new->memory == NULL) {
 		agp_free_key(new->key);
@@ -124,6 +186,7 @@
 		return NULL;
 	}
 	new->num_scratch_pages = scratch_pages;
+	new->type = AGP_NORMAL_MEMORY;
 	return new;
 }
 EXPORT_SYMBOL(agp_create_memory);
@@ -146,6 +209,11 @@
 	if (curr->is_bound == TRUE)
 		agp_unbind_memory(curr);
 
+	if (curr->type >= AGP_USER_TYPES) {
+		agp_generic_free_by_type(curr);
+		return;
+	}
+
 	if (curr->type != 0) {
 		curr->bridge->driver->free_by_type(curr);
 		return;
@@ -157,7 +225,7 @@
 		flush_agp_mappings();
 	}
 	agp_free_key(curr->key);
-	vfree(curr->memory);
+	agp_free_page_array(curr);
 	kfree(curr);
 }
 EXPORT_SYMBOL(agp_free_memory);
@@ -188,6 +256,13 @@
 	if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp)
 		return NULL;
 
+	if (type >= AGP_USER_TYPES) {
+		new = agp_generic_alloc_user(page_count, type);
+		if (new)
+			new->bridge = bridge;
+		return new;
+	}
+
 	if (type != 0) {
 		new = bridge->driver->alloc_by_type(page_count, type);
 		if (new)
@@ -960,6 +1035,7 @@
 	off_t j;
 	void *temp;
 	struct agp_bridge_data *bridge;
+	int mask_type;
 
 	bridge = mem->bridge;
 	if (!bridge)
@@ -995,7 +1071,12 @@
 	num_entries -= agp_memory_reserved/PAGE_SIZE;
 	if (num_entries < 0) num_entries = 0;
 
-	if (type != 0 || mem->type != 0) {
+	if (type != mem->type) {
+		return -EINVAL;
+	}
+
+	mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
+	if (mask_type != 0) {
 		/* The generic routines know nothing of memory types */
 		return -EINVAL;
 	}
@@ -1018,7 +1099,8 @@
 	}
 
 	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
-		writel(bridge->driver->mask_memory(bridge, mem->memory[i], mem->type), bridge->gatt_table+j);
+		writel(bridge->driver->mask_memory(bridge, mem->memory[i], mask_type),
+		       bridge->gatt_table+j);
 	}
 	readl(bridge->gatt_table+j-1);	/* PCI Posting. */
 
@@ -1032,6 +1114,7 @@
 {
 	size_t i;
 	struct agp_bridge_data *bridge;
+	int mask_type;
 
 	bridge = mem->bridge;
 	if (!bridge)
@@ -1040,7 +1123,11 @@
 	if (mem->page_count == 0)
 		return 0;
 
-	if (type != 0 || mem->type != 0) {
+	if (type != mem->type)
+		return -EINVAL;
+
+	mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
+	if (mask_type != 0) {
 		/* The generic routines know nothing of memory types */
 		return -EINVAL;
 	}
@@ -1066,12 +1153,34 @@
 
 void agp_generic_free_by_type(struct agp_memory *curr)
 {
-	vfree(curr->memory);
+	agp_free_page_array(curr);
 	agp_free_key(curr->key);
 	kfree(curr);
 }
 EXPORT_SYMBOL(agp_generic_free_by_type);
 
+struct agp_memory *agp_generic_alloc_user(size_t page_count, int type)
+{
+	struct agp_memory *new;
+	int i;
+	int pages;
+
+	pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
+	new = agp_create_user_memory(page_count);
+	if (new == NULL)
+		return NULL;
+
+	for (i = 0; i < page_count; i++) {
+		new->memory[i] = 0;
+	}
+	new->page_count = 0;
+	new->type = type;
+	new->num_scratch_pages = pages;
+
+	return new;
+}
+EXPORT_SYMBOL(agp_generic_alloc_user);
+
 
 /*
  * Basic Page Allocation Routines -
@@ -1165,6 +1274,15 @@
 }
 EXPORT_SYMBOL(agp_generic_mask_memory);
 
+int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge,
+				  int type)
+{
+	if (type >= AGP_USER_TYPES)
+		return 0;
+	return type;
+}
+EXPORT_SYMBOL(agp_generic_type_to_mask_type);
+
 /*
  * These functions are implemented according to the AGPv3 spec,
  * which covers implementation details that had previously been