mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
a030ce4477
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>
222 lines
5.3 KiB
C
222 lines
5.3 KiB
C
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/init.h>
|
|
#include <linux/agp_backend.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <asm/machvec.h>
|
|
#include <asm/agp_backend.h>
|
|
#include "../../../arch/alpha/kernel/pci_impl.h"
|
|
|
|
#include "agp.h"
|
|
|
|
static struct page *alpha_core_agp_vm_nopage(struct vm_area_struct *vma,
|
|
unsigned long address,
|
|
int *type)
|
|
{
|
|
alpha_agp_info *agp = agp_bridge->dev_private_data;
|
|
dma_addr_t dma_addr;
|
|
unsigned long pa;
|
|
struct page *page;
|
|
|
|
dma_addr = address - vma->vm_start + agp->aperture.bus_base;
|
|
pa = agp->ops->translate(agp, dma_addr);
|
|
|
|
if (pa == (unsigned long)-EINVAL)
|
|
return NULL; /* no translation */
|
|
|
|
/*
|
|
* Get the page, inc the use count, and return it
|
|
*/
|
|
page = virt_to_page(__va(pa));
|
|
get_page(page);
|
|
if (type)
|
|
*type = VM_FAULT_MINOR;
|
|
return page;
|
|
}
|
|
|
|
static struct aper_size_info_fixed alpha_core_agp_sizes[] =
|
|
{
|
|
{ 0, 0, 0 }, /* filled in by alpha_core_agp_setup */
|
|
};
|
|
|
|
struct vm_operations_struct alpha_core_agp_vm_ops = {
|
|
.nopage = alpha_core_agp_vm_nopage,
|
|
};
|
|
|
|
|
|
static int alpha_core_agp_fetch_size(void)
|
|
{
|
|
return alpha_core_agp_sizes[0].size;
|
|
}
|
|
|
|
static int alpha_core_agp_configure(void)
|
|
{
|
|
alpha_agp_info *agp = agp_bridge->dev_private_data;
|
|
agp_bridge->gart_bus_addr = agp->aperture.bus_base;
|
|
return 0;
|
|
}
|
|
|
|
static void alpha_core_agp_cleanup(void)
|
|
{
|
|
alpha_agp_info *agp = agp_bridge->dev_private_data;
|
|
|
|
agp->ops->cleanup(agp);
|
|
}
|
|
|
|
static void alpha_core_agp_tlbflush(struct agp_memory *mem)
|
|
{
|
|
alpha_agp_info *agp = agp_bridge->dev_private_data;
|
|
alpha_mv.mv_pci_tbi(agp->hose, 0, -1);
|
|
}
|
|
|
|
static void alpha_core_agp_enable(struct agp_bridge_data *bridge, u32 mode)
|
|
{
|
|
alpha_agp_info *agp = bridge->dev_private_data;
|
|
|
|
agp->mode.lw = agp_collect_device_status(bridge, mode,
|
|
agp->capability.lw);
|
|
|
|
agp->mode.bits.enable = 1;
|
|
agp->ops->configure(agp);
|
|
|
|
agp_device_command(agp->mode.lw, 0);
|
|
}
|
|
|
|
static int alpha_core_agp_insert_memory(struct agp_memory *mem, off_t pg_start,
|
|
int type)
|
|
{
|
|
alpha_agp_info *agp = agp_bridge->dev_private_data;
|
|
int num_entries, status;
|
|
void *temp;
|
|
|
|
if (type >= AGP_USER_TYPES || mem->type >= AGP_USER_TYPES)
|
|
return -EINVAL;
|
|
|
|
temp = agp_bridge->current_size;
|
|
num_entries = A_SIZE_FIX(temp)->num_entries;
|
|
if ((pg_start + mem->page_count) > num_entries)
|
|
return -EINVAL;
|
|
|
|
status = agp->ops->bind(agp, pg_start, mem);
|
|
mb();
|
|
alpha_core_agp_tlbflush(mem);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int alpha_core_agp_remove_memory(struct agp_memory *mem, off_t pg_start,
|
|
int type)
|
|
{
|
|
alpha_agp_info *agp = agp_bridge->dev_private_data;
|
|
int status;
|
|
|
|
status = agp->ops->unbind(agp, pg_start, mem);
|
|
alpha_core_agp_tlbflush(mem);
|
|
return status;
|
|
}
|
|
|
|
static int alpha_core_agp_create_free_gatt_table(struct agp_bridge_data *a)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
struct agp_bridge_driver alpha_core_agp_driver = {
|
|
.owner = THIS_MODULE,
|
|
.aperture_sizes = alpha_core_agp_sizes,
|
|
.num_aperture_sizes = 1,
|
|
.size_type = FIXED_APER_SIZE,
|
|
.cant_use_aperture = 1,
|
|
.masks = NULL,
|
|
|
|
.fetch_size = alpha_core_agp_fetch_size,
|
|
.configure = alpha_core_agp_configure,
|
|
.agp_enable = alpha_core_agp_enable,
|
|
.cleanup = alpha_core_agp_cleanup,
|
|
.tlb_flush = alpha_core_agp_tlbflush,
|
|
.mask_memory = agp_generic_mask_memory,
|
|
.cache_flush = global_cache_flush,
|
|
.create_gatt_table = alpha_core_agp_create_free_gatt_table,
|
|
.free_gatt_table = alpha_core_agp_create_free_gatt_table,
|
|
.insert_memory = alpha_core_agp_insert_memory,
|
|
.remove_memory = alpha_core_agp_remove_memory,
|
|
.alloc_by_type = agp_generic_alloc_by_type,
|
|
.free_by_type = agp_generic_free_by_type,
|
|
.agp_alloc_page = agp_generic_alloc_page,
|
|
.agp_destroy_page = agp_generic_destroy_page,
|
|
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
|
|
};
|
|
|
|
struct agp_bridge_data *alpha_bridge;
|
|
|
|
int __init
|
|
alpha_core_agp_setup(void)
|
|
{
|
|
alpha_agp_info *agp = alpha_mv.agp_info();
|
|
struct pci_dev *pdev; /* faked */
|
|
struct aper_size_info_fixed *aper_size;
|
|
|
|
if (!agp)
|
|
return -ENODEV;
|
|
if (agp->ops->setup(agp))
|
|
return -ENODEV;
|
|
|
|
/*
|
|
* Build the aperture size descriptor
|
|
*/
|
|
aper_size = alpha_core_agp_sizes;
|
|
aper_size->size = agp->aperture.size / (1024 * 1024);
|
|
aper_size->num_entries = agp->aperture.size / PAGE_SIZE;
|
|
aper_size->page_order = __ffs(aper_size->num_entries / 1024);
|
|
|
|
/*
|
|
* Build a fake pci_dev struct
|
|
*/
|
|
pdev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
|
|
if (!pdev)
|
|
return -ENOMEM;
|
|
pdev->vendor = 0xffff;
|
|
pdev->device = 0xffff;
|
|
pdev->sysdata = agp->hose;
|
|
|
|
alpha_bridge = agp_alloc_bridge();
|
|
if (!alpha_bridge)
|
|
goto fail;
|
|
|
|
alpha_bridge->driver = &alpha_core_agp_driver;
|
|
alpha_bridge->vm_ops = &alpha_core_agp_vm_ops;
|
|
alpha_bridge->current_size = aper_size; /* only 1 size */
|
|
alpha_bridge->dev_private_data = agp;
|
|
alpha_bridge->dev = pdev;
|
|
alpha_bridge->mode = agp->capability.lw;
|
|
|
|
printk(KERN_INFO PFX "Detected AGP on hose %d\n", agp->hose->index);
|
|
return agp_add_bridge(alpha_bridge);
|
|
|
|
fail:
|
|
kfree(pdev);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int __init agp_alpha_core_init(void)
|
|
{
|
|
if (agp_off)
|
|
return -EINVAL;
|
|
if (alpha_mv.agp_info)
|
|
return alpha_core_agp_setup();
|
|
return -ENODEV;
|
|
}
|
|
|
|
static void __exit agp_alpha_core_cleanup(void)
|
|
{
|
|
agp_remove_bridge(alpha_bridge);
|
|
agp_put_bridge(alpha_bridge);
|
|
}
|
|
|
|
module_init(agp_alpha_core_init);
|
|
module_exit(agp_alpha_core_cleanup);
|
|
|
|
MODULE_AUTHOR("Jeff Wiedemeier <Jeff.Wiedemeier@hp.com>");
|
|
MODULE_LICENSE("GPL and additional rights");
|