Merge branch 'x86/urgent' into x86/pat
[linux-2.6.git] / drivers / char / agp / generic.c
index 118dbde..10d6cbd 100644 (file)
@@ -201,14 +201,22 @@ void agp_free_memory(struct agp_memory *curr)
                return;
        }
        if (curr->page_count != 0) {
-               for (i = 0; i < curr->page_count; i++) {
-                       curr->memory[i] = (unsigned long)gart_to_virt(curr->memory[i]);
-                       curr->bridge->driver->agp_destroy_page((void *)curr->memory[i],
-                                                              AGP_PAGE_DESTROY_UNMAP);
-               }
-               for (i = 0; i < curr->page_count; i++) {
-                       curr->bridge->driver->agp_destroy_page((void *)curr->memory[i],
-                                                              AGP_PAGE_DESTROY_FREE);
+               if (curr->bridge->driver->agp_destroy_pages) {
+                       curr->bridge->driver->agp_destroy_pages(curr);
+               } else {
+
+                       for (i = 0; i < curr->page_count; i++) {
+                               curr->memory[i] = (unsigned long)gart_to_virt(
+                                       curr->memory[i]);
+                               curr->bridge->driver->agp_destroy_page(
+                                       (void *)curr->memory[i],
+                                       AGP_PAGE_DESTROY_UNMAP);
+                       }
+                       for (i = 0; i < curr->page_count; i++) {
+                               curr->bridge->driver->agp_destroy_page(
+                                       (void *)curr->memory[i],
+                                       AGP_PAGE_DESTROY_FREE);
+                       }
                }
        }
        agp_free_key(curr->key);
@@ -264,6 +272,15 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge,
        if (new == NULL)
                return NULL;
 
+       if (bridge->driver->agp_alloc_pages) {
+               if (bridge->driver->agp_alloc_pages(bridge, new, page_count)) {
+                       agp_free_memory(new);
+                       return NULL;
+               }
+               new->bridge = bridge;
+               return new;
+       }
+
        for (i = 0; i < page_count; i++) {
                void *addr = bridge->driver->agp_alloc_page(bridge);
 
@@ -1203,6 +1220,39 @@ EXPORT_SYMBOL(agp_generic_alloc_user);
  * against a maximum value.
  */
 
+int agp_generic_alloc_pages(struct agp_bridge_data *bridge, struct agp_memory *mem, size_t num_pages)
+{
+       struct page * page;
+       int i, ret = -ENOMEM;
+
+       for (i = 0; i < num_pages; i++) {
+               page = alloc_page(GFP_KERNEL | GFP_DMA32);
+               /* agp_free_memory() needs gart address */
+               if (page == NULL)
+                       goto out;
+
+#ifndef CONFIG_X86
+               map_page_into_agp(page);
+#endif
+               get_page(page);
+               atomic_inc(&agp_bridge->current_memory_agp);
+
+               /* set_memory_array_uc() needs virtual address */
+               mem->memory[i] = (unsigned long)page_address(page);
+               mem->page_count++;
+       }
+
+#ifdef CONFIG_X86
+       set_memory_array_uc(mem->memory, num_pages);
+#endif
+       ret = 0;
+out:
+       for (i = 0; i < mem->page_count; i++)
+               mem->memory[i] = virt_to_gart((void *)mem->memory[i]);
+       return ret;
+}
+EXPORT_SYMBOL(agp_generic_alloc_pages);
+
 void *agp_generic_alloc_page(struct agp_bridge_data *bridge)
 {
        struct page * page;
@@ -1219,6 +1269,37 @@ void *agp_generic_alloc_page(struct agp_bridge_data *bridge)
 }
 EXPORT_SYMBOL(agp_generic_alloc_page);
 
+void agp_generic_destroy_pages(struct agp_memory *mem)
+{
+       int i;
+       void *addr;
+       struct page *page;
+
+       if (!mem)
+               return;
+
+       for (i = 0; i < mem->page_count; i++)
+               mem->memory[i] = (unsigned long)gart_to_virt(mem->memory[i]);
+
+#ifdef CONFIG_X86
+       set_memory_array_wb(mem->memory, mem->page_count);
+#endif
+
+       for (i = 0; i < mem->page_count; i++) {
+               addr = (void *)mem->memory[i];
+               page = virt_to_page(addr);
+
+#ifndef CONFIG_X86
+               unmap_page_from_agp(page);
+#endif
+
+               put_page(page);
+               free_page((unsigned long)addr);
+               atomic_dec(&agp_bridge->current_memory_agp);
+               mem->memory[i] = 0;
+       }
+}
+EXPORT_SYMBOL(agp_generic_destroy_pages);
 
 void agp_generic_destroy_page(void *addr, int flags)
 {