From: Michael Werner This patch adds support for initializing and addressing multiple AGP bridges using the agpgart driver. In particular, it extends agp_acquire and agp_allocate_memory so that different bridges can be acquired and memory allocated within a specific AGP aperature. Signed-off-by: Mike Werner Signed-off-by: Andrew Morton --- 25-akpm/drivers/char/agp/agp.h | 3 25-akpm/drivers/char/agp/backend.c | 109 +++++++++++++++++++---------------- 25-akpm/drivers/char/agp/frontend.c | 30 +++++---- 25-akpm/drivers/char/agp/generic.c | 110 +++++++++++++++++++++--------------- 25-akpm/include/linux/agp_backend.h | 31 ++++++---- 5 files changed, 167 insertions(+), 116 deletions(-) diff -puN drivers/char/agp/agp.h~agpgart-allow-multiple-backends-to-be-initialized drivers/char/agp/agp.h --- 25/drivers/char/agp/agp.h~agpgart-allow-multiple-backends-to-be-initialized Tue Jan 4 16:28:30 2005 +++ 25-akpm/drivers/char/agp/agp.h Tue Jan 4 16:28:30 2005 @@ -1,5 +1,6 @@ /* * AGPGART + * Copyright (C) 2004 Silicon Graphics, Inc. * Copyright (C) 2002-2004 Dave Jones * Copyright (C) 1999 Jeff Hartmann * Copyright (C) 1999 Precision Insight, Inc. @@ -139,6 +140,7 @@ struct agp_bridge_data { int capndx; char major_version; char minor_version; + struct list_head list; }; #define KB(x) ((x) * 1024) @@ -261,6 +263,7 @@ int agp_3_5_enable(struct agp_bridge_dat void global_cache_flush(void); void get_agp_version(struct agp_bridge_data *bridge); unsigned long agp_generic_mask_memory(unsigned long addr, int type); +struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev); /* generic routines for agp>=3 */ int agp3_generic_fetch_size(void); diff -puN drivers/char/agp/backend.c~agpgart-allow-multiple-backends-to-be-initialized drivers/char/agp/backend.c --- 25/drivers/char/agp/backend.c~agpgart-allow-multiple-backends-to-be-initialized Tue Jan 4 16:28:30 2005 +++ 25-akpm/drivers/char/agp/backend.c Tue Jan 4 16:28:30 2005 @@ -1,5 +1,6 @@ /* * AGPGART driver backend routines. + * Copyright (C) 2004 Silicon Graphics, Inc. * Copyright (C) 2002-2003 Dave Jones. * Copyright (C) 1999 Jeff Hartmann. * Copyright (C) 1999 Precision Insight, Inc. @@ -42,34 +43,35 @@ * fix some real stupidity. It's only by chance we can bump * past 0.99 at all due to some boolean logic error. */ #define AGPGART_VERSION_MAJOR 0 -#define AGPGART_VERSION_MINOR 100 +#define AGPGART_VERSION_MINOR 101 static struct agp_version agp_current_version = { .major = AGPGART_VERSION_MAJOR, .minor = AGPGART_VERSION_MINOR, }; -static int agp_count=0; - -struct agp_bridge_data agp_bridge_dummy = { .type = NOT_SUPPORTED }; -struct agp_bridge_data *agp_bridge = &agp_bridge_dummy; +struct agp_bridge_data *agp_bridge; +LIST_HEAD(agp_bridges); EXPORT_SYMBOL(agp_bridge); - +EXPORT_SYMBOL(agp_bridges); /** - * agp_backend_acquire - attempt to acquire the agp backend. + * agp_backend_acquire - attempt to acquire an agp backend. * - * returns -EBUSY if agp is in use, - * returns 0 if the caller owns the agp backend */ -int agp_backend_acquire(void) +struct agp_bridge_data *agp_backend_acquire(struct pci_dev *pdev) { - if (agp_bridge->type == NOT_SUPPORTED) - return -EINVAL; - if (atomic_read(&agp_bridge->agp_in_use)) - return -EBUSY; - atomic_inc(&agp_bridge->agp_in_use); - return 0; + struct agp_bridge_data *bridge; + + bridge = agp_generic_find_bridge(pdev); + + if (!bridge) + return NULL; + + if (atomic_read(&bridge->agp_in_use)) + return NULL; + atomic_inc(&bridge->agp_in_use); + return bridge; } EXPORT_SYMBOL(agp_backend_acquire); @@ -82,10 +84,11 @@ EXPORT_SYMBOL(agp_backend_acquire); * * (Ensure that all memory it bound is unbound.) */ -void agp_backend_release(void) +void agp_backend_release(struct agp_bridge_data *bridge) { - if (agp_bridge->type != NOT_SUPPORTED) - atomic_dec(&agp_bridge->agp_in_use); + + if (bridge) + atomic_dec(&bridge->agp_in_use); } EXPORT_SYMBOL(agp_backend_release); @@ -121,7 +124,6 @@ static int agp_find_max(void) (maxes_table[index].agp - maxes_table[index - 1].agp)) / (maxes_table[index].mem - maxes_table[index - 1].mem); - printk(KERN_INFO PFX "Maximum main memory to use for agp memory: %ldM\n", result); result = result << (20 - PAGE_SHIFT); return result; } @@ -178,9 +180,6 @@ static int agp_backend_initialize(struct goto err_out; } - printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n", - size_value, bridge->gart_bus_addr); - return 0; err_out: @@ -225,16 +224,31 @@ static const drm_agp_t drm_agp = { &agp_copy_info }; -/* XXX Kludge alert: agpgart isn't ready for multiple bridges yet */ +/* When we remove the global variable agp_bridge from all drivers + * then agp_alloc_bridge and agp_generic_find_bridge need to be updated + */ + struct agp_bridge_data *agp_alloc_bridge(void) { - return agp_bridge; + struct agp_bridge_data *bridge = kmalloc(sizeof(*bridge), GFP_KERNEL); + + if (!bridge) + return NULL; + + if (list_empty(&agp_bridges)) + agp_bridge = bridge; + + return bridge; } EXPORT_SYMBOL(agp_alloc_bridge); void agp_put_bridge(struct agp_bridge_data *bridge) { + kfree(bridge); + + if (list_empty(&agp_bridges)) + agp_bridge = NULL; } EXPORT_SYMBOL(agp_put_bridge); @@ -251,43 +265,41 @@ int agp_add_bridge(struct agp_bridge_dat return -EINVAL; } - if (agp_count) { - printk (KERN_INFO PFX - "Only one agpgart device currently supported.\n"); - return -ENODEV; - } - /* Grab reference on the chipset driver. */ if (!try_module_get(bridge->driver->owner)) { printk (KERN_INFO PFX "Couldn't lock chipset driver.\n"); return -EINVAL; } - bridge->type = SUPPORTED; - - error = agp_backend_initialize(agp_bridge); + error = agp_backend_initialize(bridge); if (error) { printk (KERN_INFO PFX "agp_backend_initialize() failed.\n"); goto err_out; } - error = agp_frontend_initialize(); - if (error) { - printk (KERN_INFO PFX "agp_frontend_initialize() failed.\n"); - goto frontend_err; - } + if (list_empty(&agp_bridges)) { + error = agp_frontend_initialize(); + if (error) { + printk (KERN_INFO PFX "agp_frontend_initialize() failed.\n"); + goto frontend_err; + } + + /* FIXME: What to do with this? */ + inter_module_register("drm_agp", THIS_MODULE, &drm_agp); + + printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n", + bridge->driver->fetch_size(), bridge->gart_bus_addr); - /* FIXME: What to do with this? */ - inter_module_register("drm_agp", THIS_MODULE, &drm_agp); + } - agp_count++; + list_add(&bridge->list, &agp_bridges); return 0; frontend_err: - agp_backend_cleanup(agp_bridge); + agp_backend_cleanup(bridge); err_out: - bridge->type = NOT_SUPPORTED; module_put(bridge->driver->owner); + agp_put_bridge(bridge); return error; } EXPORT_SYMBOL_GPL(agp_add_bridge); @@ -295,11 +307,12 @@ EXPORT_SYMBOL_GPL(agp_add_bridge); void agp_remove_bridge(struct agp_bridge_data *bridge) { - bridge->type = NOT_SUPPORTED; - agp_frontend_cleanup(); agp_backend_cleanup(bridge); - inter_module_unregister("drm_agp"); - agp_count--; + list_del(&bridge->list); + if (list_empty(&agp_bridges)) { + agp_frontend_cleanup(); + inter_module_unregister("drm_agp"); + } module_put(bridge->driver->owner); } EXPORT_SYMBOL_GPL(agp_remove_bridge); diff -puN drivers/char/agp/frontend.c~agpgart-allow-multiple-backends-to-be-initialized drivers/char/agp/frontend.c --- 25/drivers/char/agp/frontend.c~agpgart-allow-multiple-backends-to-be-initialized Tue Jan 4 16:28:30 2005 +++ 25-akpm/drivers/char/agp/frontend.c Tue Jan 4 16:28:30 2005 @@ -1,5 +1,6 @@ /* * AGPGART driver frontend + * Copyright (C) 2004 Silicon Graphics, Inc. * Copyright (C) 2002-2003 Dave Jones * Copyright (C) 1999 Jeff Hartmann * Copyright (C) 1999 Precision Insight, Inc. @@ -299,7 +300,7 @@ static struct agp_memory *agp_allocate_m { struct agp_memory *memory; - memory = agp_allocate_memory(pg_count, type); + memory = agp_allocate_memory(agp_bridge, pg_count, type); if (memory == NULL) return NULL; @@ -420,7 +421,7 @@ static int agp_remove_controller(struct if (agp_fe.current_controller == controller) { agp_fe.current_controller = NULL; agp_fe.backend_acquired = FALSE; - agp_backend_release(); + agp_backend_release(agp_bridge); } kfree(controller); return 0; @@ -468,7 +469,7 @@ static void agp_controller_release_curre agp_fe.current_controller = NULL; agp_fe.used_by_controller = FALSE; - agp_backend_release(); + agp_backend_release(agp_bridge); } /* @@ -605,7 +606,7 @@ static int agp_mmap(struct file *file, s if (!(test_bit(AGP_FF_IS_VALID, &priv->access_flags))) goto out_eperm; - agp_copy_info(&kerninfo); + agp_copy_info(agp_bridge, &kerninfo); size = vma->vm_end - vma->vm_start; current_size = kerninfo.aper_size; current_size = current_size * 0x100000; @@ -757,7 +758,7 @@ static int agpioc_info_wrap(struct agp_f struct agp_info userinfo; struct agp_kern_info kerninfo; - agp_copy_info(&kerninfo); + agp_copy_info(agp_bridge, &kerninfo); userinfo.version.major = kerninfo.version.major; userinfo.version.minor = kerninfo.version.minor; @@ -777,7 +778,6 @@ static int agpioc_info_wrap(struct agp_f static int agpioc_acquire_wrap(struct agp_file_private *priv) { - int ret; struct agp_controller *controller; DBG(""); @@ -788,11 +788,15 @@ static int agpioc_acquire_wrap(struct ag if (agp_fe.current_controller != NULL) return -EBUSY; - ret = agp_backend_acquire(); - if (ret == 0) - agp_fe.backend_acquired = TRUE; - else - return ret; + if(!agp_bridge) + return -ENODEV; + + if (atomic_read(&agp_bridge->agp_in_use)) + return -EBUSY; + + atomic_inc(&agp_bridge->agp_in_use); + + agp_fe.backend_acquired = TRUE; controller = agp_find_controller_by_pid(priv->my_pid); @@ -803,7 +807,7 @@ static int agpioc_acquire_wrap(struct ag if (controller == NULL) { agp_fe.backend_acquired = FALSE; - agp_backend_release(); + agp_backend_release(agp_bridge); return -ENOMEM; } agp_insert_controller(controller); @@ -830,7 +834,7 @@ static int agpioc_setup_wrap(struct agp_ if (copy_from_user(&mode, arg, sizeof(struct agp_setup))) return -EFAULT; - agp_enable(mode.agp_mode); + agp_enable(agp_bridge, mode.agp_mode); return 0; } diff -puN drivers/char/agp/generic.c~agpgart-allow-multiple-backends-to-be-initialized drivers/char/agp/generic.c --- 25/drivers/char/agp/generic.c~agpgart-allow-multiple-backends-to-be-initialized Tue Jan 4 16:28:30 2005 +++ 25-akpm/drivers/char/agp/generic.c Tue Jan 4 16:28:30 2005 @@ -1,5 +1,6 @@ /* * AGPGART driver. + * Copyright (C) 2004 Silicon Graphics, Inc. * Copyright (C) 2002-2004 Dave Jones. * Copyright (C) 1999 Jeff Hartmann. * Copyright (C) 1999 Precision Insight, Inc. @@ -139,19 +140,19 @@ void agp_free_memory(struct agp_memory * { size_t i; - if ((agp_bridge->type == NOT_SUPPORTED) || (curr == NULL)) + if (curr == NULL) return; if (curr->is_bound == TRUE) agp_unbind_memory(curr); if (curr->type != 0) { - agp_bridge->driver->free_by_type(curr); + curr->bridge->driver->free_by_type(curr); return; } if (curr->page_count != 0) { for (i = 0; i < curr->page_count; i++) { - agp_bridge->driver->agp_destroy_page(phys_to_virt(curr->memory[i])); + curr->bridge->driver->agp_destroy_page(phys_to_virt(curr->memory[i])); } } agp_free_key(curr->key); @@ -173,20 +174,23 @@ EXPORT_SYMBOL(agp_free_memory); * * It returns NULL whenever memory is unavailable. */ -struct agp_memory *agp_allocate_memory(size_t page_count, u32 type) +struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, + size_t page_count, u32 type) { int scratch_pages; struct agp_memory *new; size_t i; - if (agp_bridge->type == NOT_SUPPORTED) + if (!bridge) return NULL; - if ((atomic_read(&agp_bridge->current_memory_agp) + page_count) > agp_bridge->max_memory_agp) + if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) return NULL; if (type != 0) { - new = agp_bridge->driver->alloc_by_type(page_count, type); + new = bridge->driver->alloc_by_type(page_count, type); + if (new) + new->bridge = bridge; return new; } @@ -198,7 +202,7 @@ struct agp_memory *agp_allocate_memory(s return NULL; for (i = 0; i < page_count; i++) { - void *addr = agp_bridge->driver->agp_alloc_page(); + void *addr = bridge->driver->agp_alloc_page(); if (addr == NULL) { agp_free_memory(new); @@ -297,26 +301,25 @@ EXPORT_SYMBOL_GPL(agp_num_entries); * This function copies information about the agp bridge device and the state of * the agp backend into an agp_kern_info pointer. */ -int agp_copy_info(struct agp_kern_info *info) +int agp_copy_info(struct agp_bridge_data *bridge, struct agp_kern_info *info) { memset(info, 0, sizeof(struct agp_kern_info)); - if (!agp_bridge || agp_bridge->type == NOT_SUPPORTED || - !agp_bridge->version) { + if (!bridge) { info->chipset = NOT_SUPPORTED; return -EIO; } - info->version.major = agp_bridge->version->major; - info->version.minor = agp_bridge->version->minor; - info->chipset = agp_bridge->type; - info->device = agp_bridge->dev; - info->mode = agp_bridge->mode; - info->aper_base = agp_bridge->gart_bus_addr; + info->version.major = bridge->version->major; + info->version.minor = bridge->version->minor; + info->chipset = SUPPORTED; + info->device = bridge->dev; + info->mode = bridge->mode; + info->aper_base = bridge->gart_bus_addr; info->aper_size = agp_return_size(); - info->max_memory = agp_bridge->max_memory_agp; - info->current_memory = atomic_read(&agp_bridge->current_memory_agp); - info->cant_use_aperture = agp_bridge->driver->cant_use_aperture; - info->vm_ops = agp_bridge->vm_ops; + info->max_memory = bridge->max_memory_agp; + info->current_memory = atomic_read(&bridge->current_memory_agp); + info->cant_use_aperture = bridge->driver->cant_use_aperture; + info->vm_ops = bridge->vm_ops; info->page_mask = ~0UL; return 0; } @@ -345,7 +348,7 @@ int agp_bind_memory(struct agp_memory *c { int ret_val; - if ((agp_bridge->type == NOT_SUPPORTED) || (curr == NULL)) + if (curr == NULL) return -EINVAL; if (curr->is_bound == TRUE) { @@ -353,10 +356,10 @@ int agp_bind_memory(struct agp_memory *c return -EINVAL; } if (curr->is_flushed == FALSE) { - agp_bridge->driver->cache_flush(); + curr->bridge->driver->cache_flush(); curr->is_flushed = TRUE; } - ret_val = agp_bridge->driver->insert_memory(curr, pg_start, curr->type); + ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); if (ret_val != 0) return ret_val; @@ -380,7 +383,7 @@ int agp_unbind_memory(struct agp_memory { int ret_val; - if ((agp_bridge->type == NOT_SUPPORTED) || (curr == NULL)) + if (curr == NULL) return -EINVAL; if (curr->is_bound != TRUE) { @@ -388,7 +391,7 @@ int agp_unbind_memory(struct agp_memory return -EINVAL; } - ret_val = agp_bridge->driver->remove_memory(curr, curr->pg_start, curr->type); + ret_val = curr->bridge->driver->remove_memory(curr, curr->pg_start, curr->type); if (ret_val != 0) return ret_val; @@ -591,12 +594,12 @@ void get_agp_version(struct agp_bridge_d u32 ncapid; /* Exit early if already set by errata workarounds. */ - if (agp_bridge->major_version != 0) + if (bridge->major_version != 0) return; - pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx, &ncapid); - agp_bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; - agp_bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf; + pci_read_config_dword(bridge->dev, bridge->capndx, &ncapid); + bridge->major_version = (ncapid >> AGP_MAJOR_VERSION_SHIFT) & 0xf; + bridge->minor_version = (ncapid >> AGP_MINOR_VERSION_SHIFT) & 0xf; } EXPORT_SYMBOL(get_agp_version); @@ -828,10 +831,15 @@ int agp_generic_insert_memory(struct agp size_t i; off_t j; void *temp; + struct agp_bridge_data *bridge; - temp = agp_bridge->current_size; + bridge = mem->bridge; + if (!bridge) + return -EINVAL; - switch (agp_bridge->driver->size_type) { + temp = bridge->current_size; + + switch (bridge->driver->size_type) { case U8_APER_SIZE: num_entries = A_SIZE_8(temp)->num_entries; break; @@ -868,22 +876,22 @@ int agp_generic_insert_memory(struct agp j = pg_start; while (j < (pg_start + mem->page_count)) { - if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j))) + if (!PGE_EMPTY(bridge, readl(bridge->gatt_table+j))) return -EBUSY; j++; } if (mem->is_flushed == FALSE) { - agp_bridge->driver->cache_flush(); + bridge->driver->cache_flush(); mem->is_flushed = TRUE; } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(mem->memory[i], mem->type), agp_bridge->gatt_table+j); - readl(agp_bridge->gatt_table+j); /* PCI Posting. */ + writel(bridge->driver->mask_memory(mem->memory[i], mem->type), bridge->gatt_table+j); + readl(bridge->gatt_table+j); /* PCI Posting. */ } - agp_bridge->driver->tlb_flush(mem); + bridge->driver->tlb_flush(mem); return 0; } EXPORT_SYMBOL(agp_generic_insert_memory); @@ -892,6 +900,11 @@ EXPORT_SYMBOL(agp_generic_insert_memory) int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) { size_t i; + struct agp_bridge_data *bridge; + + bridge = mem->bridge; + if (!bridge) + return -EINVAL; if (type != 0 || mem->type != 0) { /* The generic routines know nothing of memory types */ @@ -900,12 +913,12 @@ int agp_generic_remove_memory(struct agp /* AK: bogus, should encode addresses > 4GB */ for (i = pg_start; i < (mem->page_count + pg_start); i++) { - writel(agp_bridge->scratch_page, agp_bridge->gatt_table+i); - readl(agp_bridge->gatt_table+i); /* PCI Posting. */ + writel(bridge->scratch_page, bridge->gatt_table+i); + readl(bridge->gatt_table+i); /* PCI Posting. */ } global_cache_flush(); - agp_bridge->driver->tlb_flush(mem); + bridge->driver->tlb_flush(mem); return 0; } EXPORT_SYMBOL(agp_generic_remove_memory); @@ -978,14 +991,25 @@ EXPORT_SYMBOL(agp_generic_destroy_page); * * @mode: agp mode register value to configure with. */ -void agp_enable(u32 mode) +void agp_enable(struct agp_bridge_data *bridge, u32 mode) { - if (agp_bridge->type == NOT_SUPPORTED) + if (!bridge) return; - agp_bridge->driver->agp_enable(mode); + bridge->driver->agp_enable(mode); } EXPORT_SYMBOL(agp_enable); +/* When we remove the global variable agp_bridge from all drivers + * then agp_alloc_bridge and agp_generic_find_bridge need to be updated + */ + +struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev) +{ + if (list_empty(&agp_bridges)) + return NULL; + + return agp_bridge; +} static void ipi_handler(void *null) { diff -puN include/linux/agp_backend.h~agpgart-allow-multiple-backends-to-be-initialized include/linux/agp_backend.h --- 25/include/linux/agp_backend.h~agpgart-allow-multiple-backends-to-be-initialized Tue Jan 4 16:28:30 2005 +++ 25-akpm/include/linux/agp_backend.h Tue Jan 4 16:28:30 2005 @@ -1,6 +1,7 @@ /* * AGPGART backend specific includes. Not for userspace consumption. * + * Copyright (C) 2004 Silicon Graphics, Inc. * Copyright (C) 2002-2003 Dave Jones * Copyright (C) 1999 Jeff Hartmann * Copyright (C) 1999 Precision Insight, Inc. @@ -71,13 +72,16 @@ struct agp_kern_info { * the items to detrimine the status of this block of agp memory. */ +struct agp_bridge_data; + struct agp_memory { - int key; struct agp_memory *next; struct agp_memory *prev; + struct agp_bridge_data *bridge; + unsigned long *memory; size_t page_count; + int key; int num_scratch_pages; - unsigned long *memory; off_t pg_start; u32 type; u32 physical; @@ -87,14 +91,17 @@ struct agp_memory { #define AGP_NORMAL_MEMORY 0 +extern struct agp_bridge_data *agp_bridge; +extern struct list_head agp_bridges; + extern void agp_free_memory(struct agp_memory *); -extern struct agp_memory *agp_allocate_memory(size_t, u32); -extern int agp_copy_info(struct agp_kern_info *); +extern struct agp_memory *agp_allocate_memory(struct agp_bridge_data *, size_t, u32); +extern int agp_copy_info(struct agp_bridge_data *, struct agp_kern_info *); extern int agp_bind_memory(struct agp_memory *, off_t); extern int agp_unbind_memory(struct agp_memory *); -extern void agp_enable(u32); -extern int agp_backend_acquire(void); -extern void agp_backend_release(void); +extern void agp_enable(struct agp_bridge_data *, u32); +extern struct agp_bridge_data *agp_backend_acquire(struct pci_dev *); +extern void agp_backend_release(struct agp_bridge_data *); /* * Interface between drm and agp code. When agp initializes, it makes @@ -103,13 +110,13 @@ extern void agp_backend_release(void); */ typedef struct { void (*free_memory)(struct agp_memory *); - struct agp_memory * (*allocate_memory)(size_t, u32); + struct agp_memory * (*allocate_memory)(struct agp_bridge_data *, size_t, u32); int (*bind_memory)(struct agp_memory *, off_t); int (*unbind_memory)(struct agp_memory *); - void (*enable)(u32); - int (*acquire)(void); - void (*release)(void); - int (*copy_info)(struct agp_kern_info *); + void (*enable)(struct agp_bridge_data *, u32); + struct agp_bridge_data *(*acquire)(struct pci_dev *); + void (*release)(struct agp_bridge_data *); + int (*copy_info)(struct agp_bridge_data *, struct agp_kern_info *); } drm_agp_t; extern const drm_agp_t *drm_agp_p; _