Commit aeac6f3a authored by Dave Jones's avatar Dave Jones Committed by Dave Jones

[AGPGART] allow multiple backends to be initialized.

From: Michael Werner <werner@mrcoffee.engr.sgi.com>

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.

From: Brice Goglin <Brice.Goglin@ens-lyon.fr>

It seems that memsetting the whole bridge structure to 0 (instead of juste
the agp_in_use field) fixes Benoit's problem too.  No idea which field was
responsible for this.  New patch attached.

From: Mike Werner <werner@sgi.com>

add bridge assignment missed in agp_allocate_memory

From: Andi Kleen <ak@suse.de>

Here's the correct fix. agp_bridge is defined in drivers/char/agp/agp.h.
It's a bit ugly though.
Signed-off-by: default avatarMike Werner <werner@sgi.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent 39f7a73c
......@@ -789,7 +789,7 @@ static int __init pci_iommu_init(void)
/* Add other K8 AGP bridge drivers here */
no_agp = no_agp ||
(agp_amd64_init() < 0) ||
(agp_copy_info(&info) < 0);
(agp_copy_info(agp_bridge, &info) < 0);
#endif
if (swiotlb) {
......
/*
* AGPGART
* Copyright (C) 2004 Silicon Graphics, Inc.
* Copyright (C) 2002-2004 Dave Jones
* Copyright (C) 1999 Jeff Hartmann
* Copyright (C) 1999 Precision Insight, Inc.
......@@ -140,6 +141,7 @@ struct agp_bridge_data {
int flags;
char major_version;
char minor_version;
struct list_head list;
};
#define KB(x) ((x) * 1024)
......@@ -262,6 +264,7 @@ int agp_3_5_enable(struct agp_bridge_data *bridge);
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);
......
/*
* 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 agp_bridge_data *bridge)
goto err_out;
}
printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n",
size_value, bridge->gart_bus_addr);
return 0;
err_out:
......@@ -214,16 +213,35 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge)
phys_to_virt(bridge->scratch_page_real));
}
/* 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;
memset(bridge, 0, sizeof(*bridge));
atomic_set(&bridge->agp_in_use, 0);
atomic_set(&bridge->current_memory_agp, 0);
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);
......@@ -240,40 +258,38 @@ int agp_add_bridge(struct agp_bridge_data *bridge)
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;
}
printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n",
bridge->driver->fetch_size(), bridge->gart_bus_addr);
}
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);
......@@ -281,10 +297,10 @@ 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);
agp_count--;
list_del(&bridge->list);
if (list_empty(&agp_bridges))
agp_frontend_cleanup();
module_put(bridge->driver->owner);
}
EXPORT_SYMBOL_GPL(agp_remove_bridge);
......
/*
* 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_memory_wrap(size_t pg_count, u32 type)
{
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 agp_controller *controller)
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_current(struct agp_controller *controller,
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, struct vm_area_struct *vma)
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_file_private *priv, void __user *arg)
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_file_private *priv, void __user *arg)
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 agp_file_private *priv)
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 agp_file_private *priv)
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_file_private *priv, void __user *arg)
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;
}
......
/*
* AGPGART driver.
* Copyright (C) 2004 Silicon Graphics, Inc.
* Copyright (C) 2002-2005 Dave Jones.
* Copyright (C) 1999 Jeff Hartmann.
* Copyright (C) 1999 Precision Insight, Inc.
......@@ -139,19 +140,19 @@ void agp_free_memory(struct agp_memory *curr)
{
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(size_t page_count, u32 type)
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);
......@@ -207,6 +211,7 @@ struct agp_memory *agp_allocate_memory(size_t page_count, u32 type)
new->memory[i] = virt_to_phys(addr);
new->page_count++;
}
new->bridge = bridge;
flush_agp_mappings();
......@@ -310,38 +315,36 @@ static int check_bridge_mode(struct pci_dev *dev)
* 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;
if (check_bridge_mode(agp_bridge->dev))
info->mode = agp_bridge->mode & ~AGP3_RESERVED_MASK;
info->version.major = bridge->version->major;
info->version.minor = bridge->version->minor;
info->chipset = SUPPORTED;
info->device = bridge->dev;
if (check_bridge_mode(bridge->dev))
info->mode = bridge->mode & ~AGP3_RESERVED_MASK;
else
info->mode = agp_bridge->mode & ~AGP2_RESERVED_MASK;
info->aper_base = agp_bridge->gart_bus_addr;
info->mode = bridge->mode & ~AGP2_RESERVED_MASK;
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;
}
EXPORT_SYMBOL(agp_copy_info);
/* End - Routine to copy over information structure */
/*
* Routines for handling swapping of agp_memory into the GATT -
* These routines take agp_memory and insert them into the GATT.
......@@ -361,7 +364,7 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start)
{
int ret_val;
if ((agp_bridge->type == NOT_SUPPORTED) || (curr == NULL))
if (curr == NULL)
return -EINVAL;
if (curr->is_bound == TRUE) {
......@@ -369,10 +372,10 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start)
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;
......@@ -396,7 +399,7 @@ int agp_unbind_memory(struct agp_memory *curr)
{
int ret_val;
if ((agp_bridge->type == NOT_SUPPORTED) || (curr == NULL))
if (curr == NULL)
return -EINVAL;
if (curr->is_bound != TRUE) {
......@@ -404,7 +407,7 @@ int agp_unbind_memory(struct agp_memory *curr)
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;
......@@ -707,12 +710,12 @@ void get_agp_version(struct agp_bridge_data *bridge)
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);
......@@ -943,10 +946,15 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
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;
......@@ -983,22 +991,22 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
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);
......@@ -1007,6 +1015,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 */
......@@ -1015,12 +1028,12 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
/* 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);
......@@ -1093,14 +1106,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)
{
......
/*
* 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 *);
#endif /* __KERNEL__ */
#endif /* _AGP_BACKEND_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment