From 2a4ceb6d3e6a566cb4a9dc8f974177f031d27cd7 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 27 Jul 2009 10:27:29 +0100 Subject: agp: Switch mask_memory() method to take address argument again, not page In commit 07613ba2 ("agp: switch AGP to use page array instead of unsigned long array") we switched the mask_memory() method to take a 'struct page *' instead of an address. This is painful, because in some cases it has to be an IOMMU-mapped virtual bus address (in fact, shouldn't it _always_ be a dma_addr_t returned from pci_map_xxx(), and we just happen to get lucky most of the time?) Signed-off-by: David Woodhouse --- drivers/char/agp/agp.h | 4 ++-- drivers/char/agp/amd-k7-agp.c | 4 +++- drivers/char/agp/amd64-agp.c | 3 ++- drivers/char/agp/ati-agp.c | 3 ++- drivers/char/agp/backend.c | 4 ++-- drivers/char/agp/generic.c | 7 ++++--- drivers/char/agp/hp-agp.c | 4 +--- drivers/char/agp/i460-agp.c | 13 +++---------- drivers/char/agp/intel-agp.c | 14 +++++++------- drivers/char/agp/nvidia-agp.c | 2 +- drivers/char/agp/parisc-agp.c | 12 ++---------- drivers/char/agp/sgi-agp.c | 8 ++++---- drivers/char/agp/sworks-agp.c | 4 +++- 13 files changed, 36 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 178e2e9e9f0..ce110a3bf29 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -107,7 +107,7 @@ struct agp_bridge_driver { void (*agp_enable)(struct agp_bridge_data *, u32); void (*cleanup)(void); void (*tlb_flush)(struct agp_memory *); - unsigned long (*mask_memory)(struct agp_bridge_data *, struct page *, int); + unsigned long (*mask_memory)(struct agp_bridge_data *, dma_addr_t, int); void (*cache_flush)(void); int (*create_gatt_table)(struct agp_bridge_data *); int (*free_gatt_table)(struct agp_bridge_data *); @@ -291,7 +291,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(struct agp_bridge_data *bridge, - struct page *page, int type); + dma_addr_t phys, int type); int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, int type); struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev); diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index ba9bde71eaa..542a87895ae 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -325,7 +325,9 @@ static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type) addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_generic_mask_memory(agp_bridge, - mem->pages[i], mem->type), cur_gatt+GET_GATT_OFF(addr)); + phys_to_gart(page_to_phys(mem->pages[i])), + mem->type), + cur_gatt+GET_GATT_OFF(addr)); readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ } amd_irongate_tlbflush(mem); diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 3bf5dda90f4..e85a5b3e952 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -79,7 +79,8 @@ static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type) for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { tmp = agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type); + phys_to_gart(page_to_phys(mem->pages[i])), + mask_type); BUG_ON(tmp & 0xffffff0000000ffcULL); pte = (tmp & 0x000000ff00000000ULL) >> 28; diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 33656e144cc..59ebd60c1b6 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -302,7 +302,8 @@ static int ati_insert_memory(struct agp_memory * mem, addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mem->type), + phys_to_gart(page_to_phys(mem->pages[i])), + mem->type), cur_gatt+GET_GATT_OFF(addr)); } readl(GET_GATT(agp_bridge->gart_bus_addr)); /* PCI posting */ diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index cfa5a649dfe..3bd7e503de4 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -150,8 +150,8 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) } bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); - bridge->scratch_page = - bridge->driver->mask_memory(bridge, page, 0); + bridge->scratch_page = bridge->driver->mask_memory(bridge, + phys_to_gart(page_to_phys(page)), 0); } size_value = bridge->driver->fetch_size(); diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 1e8b461b91f..a3bcc7ef42f 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1132,7 +1132,9 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(bridge->driver->mask_memory(bridge, mem->pages[i], mask_type), + writel(bridge->driver->mask_memory(bridge, + phys_to_gart(page_to_phys(mem->pages[i])), + mask_type), bridge->gatt_table+j); } readl(bridge->gatt_table+j-1); /* PCI Posting. */ @@ -1347,9 +1349,8 @@ void global_cache_flush(void) EXPORT_SYMBOL(global_cache_flush); unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); /* memory type is ignored in the generic routine */ if (bridge->driver->masks) return addr | bridge->driver->masks[0].mask; diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 8f3d4c18491..64dbf4b1cf2 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -394,10 +394,8 @@ hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type) } static unsigned long -hp_zx1_mask_memory (struct agp_bridge_data *bridge, - struct page *page, int type) +hp_zx1_mask_memory (struct agp_bridge_data *bridge, dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); return HP_ZX1_PDIR_VALID_BIT | addr; } diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 60cc35bb5db..54191f86053 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -61,7 +61,7 @@ #define WR_FLUSH_GATT(index) RD_GATT(index) static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, - unsigned long addr, int type); + dma_addr_t addr, int type); static struct { void *gatt; /* ioremap'd GATT area */ @@ -546,20 +546,13 @@ static void i460_destroy_page (struct page *page, int flags) #endif /* I460_LARGE_IO_PAGES */ static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, - unsigned long addr, int type) + dma_addr_t addr, int type) { /* Make sure the returned address is a valid GATT entry */ return bridge->driver->masks[0].mask | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xfffff000) >> 12); } -static unsigned long i460_page_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) -{ - unsigned long addr = phys_to_gart(page_to_phys(page)); - return i460_mask_memory(bridge, addr, type); -} - const struct agp_bridge_driver intel_i460_driver = { .owner = THIS_MODULE, .aperture_sizes = i460_sizes, @@ -569,7 +562,7 @@ const struct agp_bridge_driver intel_i460_driver = { .fetch_size = i460_fetch_size, .cleanup = i460_cleanup, .tlb_flush = i460_tlb_flush, - .mask_memory = i460_page_mask_memory, + .mask_memory = i460_mask_memory, .masks = i460_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 8c9d50db5c3..21983456d67 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -343,7 +343,7 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, global_cache_flush(); for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], + phys_to_gart(page_to_phys(mem->pages[i])), mask_type), intel_private.registers+I810_PTE_BASE+(j*4)); } @@ -461,9 +461,8 @@ static void intel_i810_free_by_type(struct agp_memory *curr) } static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); /* Type checking must be done elsewhere */ return addr | bridge->driver->masks[type].mask; } @@ -851,7 +850,7 @@ static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type), + phys_to_gart(page_to_phys(mem->pages[i])), mask_type), intel_private.registers+I810_PTE_BASE+(j*4)); } readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); @@ -1081,7 +1080,9 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type), intel_private.gtt+j); + phys_to_gart(page_to_phys(mem->pages[i])), + mask_type), + intel_private.gtt+j); } readl(intel_private.gtt+j-1); @@ -1196,9 +1197,8 @@ static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) * this conditional. */ static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - dma_addr_t addr = phys_to_gart(page_to_phys(page)); /* Shift high bits down */ addr |= (addr >> 28) & 0xf0; diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index 263d71dd441..cedacee30ec 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -225,7 +225,7 @@ static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type), + phys_to_gart(page_to_phys(mem->pages[i])), mask_type), agp_bridge->gatt_table+nvidia_private.pg_offset+j); } diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index f4bb43fb801..1c129211302 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -32,7 +32,7 @@ #define AGP8X_MODE (1 << AGP8X_MODE_BIT) static unsigned long -parisc_agp_mask_memory(struct agp_bridge_data *bridge, unsigned long addr, +parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, int type); static struct _parisc_agp_info { @@ -189,20 +189,12 @@ parisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type) } static unsigned long -parisc_agp_mask_memory(struct agp_bridge_data *bridge, unsigned long addr, +parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, int type) { return SBA_PDIR_VALID_BIT | addr; } -static unsigned long -parisc_agp_page_mask_memory(struct agp_bridge_data *bridge, struct page *page, - int type) -{ - unsigned long addr = phys_to_gart(page_to_phys(page)); - return SBA_PDIR_VALID_BIT | addr; -} - static void parisc_agp_enable(struct agp_bridge_data *bridge, u32 mode) { diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index d3ea2e4226b..0d47fa84740 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -70,10 +70,9 @@ static void sgi_tioca_tlbflush(struct agp_memory *mem) * entry. */ static unsigned long -sgi_tioca_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) +sgi_tioca_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, + int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); return tioca_physpage_to_gart(addr); } @@ -190,7 +189,8 @@ static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { table[j] = - bridge->driver->mask_memory(bridge, mem->pages[i], + bridge->driver->mask_memory(bridge, + phys_to_gart(page_to_phys(mem->pages[i])), mem->type); } diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index b964a219932..07259952fc3 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -349,7 +349,9 @@ static int serverworks_insert_memory(struct agp_memory *mem, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = SVRWRKS_GET_GATT(addr); - writel(agp_bridge->driver->mask_memory(agp_bridge, mem->pages[i], mem->type), cur_gatt+GET_GATT_OFF(addr)); + writel(agp_bridge->driver->mask_memory(agp_bridge, + phys_to_gart(page_to_phys(mem->pages[i])), mem->type), + cur_gatt+GET_GATT_OFF(addr)); } serverworks_tlbflush(mem); return 0; -- cgit v1.2.3 From ff663cf8705bea101d5f73cf471855c85242575e Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Thu, 23 Jul 2009 17:25:49 +0100 Subject: agp: Add generic support for graphics dma remapping New driver hooks for support graphics memory dma remapping are introduced in this patch. It makes generic code can tell if current device needs dma remapping, then call driver provided interfaces for mapping and unmapping. Change has also been made to handle scratch_page in remapping case. Signed-off-by: Zhenyu Wang Signed-off-by: David Woodhouse --- drivers/char/agp/agp.h | 6 ++++++ drivers/char/agp/backend.c | 20 ++++++++++++++++++++ drivers/char/agp/generic.c | 9 +++++++++ include/linux/agp_backend.h | 6 +++++- 4 files changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index ce110a3bf29..17e6d0d3ba3 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -121,6 +121,11 @@ struct agp_bridge_driver { void (*agp_destroy_pages)(struct agp_memory *); int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); void (*chipset_flush)(struct agp_bridge_data *); + + int (*agp_map_page)(void *addr, dma_addr_t *ret); + void (*agp_unmap_page)(void *addr, dma_addr_t dma); + int (*agp_map_memory)(struct agp_memory *mem); + void (*agp_unmap_memory)(struct agp_memory *mem); }; struct agp_bridge_data { @@ -135,6 +140,7 @@ struct agp_bridge_data { u32 *gatt_table_real; unsigned long scratch_page; unsigned long scratch_page_real; + dma_addr_t scratch_page_dma; unsigned long gart_bus_addr; unsigned long gatt_bus_addr; u32 mode; diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 3bd7e503de4..19ac3663acd 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -152,6 +152,15 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); bridge->scratch_page = bridge->driver->mask_memory(bridge, phys_to_gart(page_to_phys(page)), 0); + + if (bridge->driver->agp_map_page && + bridge->driver->agp_map_page(phys_to_virt(page_to_phys(page)), + &bridge->scratch_page_dma)) { + dev_err(&bridge->dev->dev, + "unable to dma-map scratch page\n"); + rc = -ENOMEM; + goto err_out_nounmap; + } } size_value = bridge->driver->fetch_size(); @@ -191,6 +200,13 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) return 0; err_out: + if (bridge->driver->needs_scratch_page && + bridge->driver->agp_unmap_page) { + void *va = gart_to_virt(bridge->scratch_page_real); + + bridge->driver->agp_unmap_page(va, bridge->scratch_page_dma); + } +err_out_nounmap: if (bridge->driver->needs_scratch_page) { void *va = gart_to_virt(bridge->scratch_page_real); @@ -221,6 +237,10 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) bridge->driver->needs_scratch_page) { void *va = gart_to_virt(bridge->scratch_page_real); + if (bridge->driver->agp_unmap_page) + bridge->driver->agp_unmap_page(va, + bridge->scratch_page_dma); + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); } diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index a3bcc7ef42f..28f0208c66a 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -437,6 +437,12 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start) curr->bridge->driver->cache_flush(); curr->is_flushed = true; } + + if (curr->bridge->driver->agp_map_memory) { + ret_val = curr->bridge->driver->agp_map_memory(curr); + if (ret_val) + return ret_val; + } ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); if (ret_val != 0) @@ -478,6 +484,9 @@ int agp_unbind_memory(struct agp_memory *curr) if (ret_val != 0) return ret_val; + if (curr->bridge->driver->agp_unmap_memory) + curr->bridge->driver->agp_unmap_memory(curr); + curr->is_bound = false; curr->pg_start = 0; spin_lock(&curr->bridge->mapped_lock); diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index 76fa794fdac..8a294d65b9b 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -79,9 +79,13 @@ struct agp_memory { u32 physical; bool is_bound; bool is_flushed; - bool vmalloc_flag; + bool vmalloc_flag; + bool sg_vmalloc_flag; /* list of agp_memory mapped to the aperture */ struct list_head mapped_list; + /* DMA-mapped addresses */ + struct scatterlist *sg_list; + int num_sg; }; #define AGP_NORMAL_MEMORY 0 -- cgit v1.2.3 From 176616814d700f19914d8509d9f65dec51a6ebf7 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Mon, 27 Jul 2009 12:59:57 +0100 Subject: intel_agp: Use PCI DMA API correctly on chipsets new enough to have IOMMU When graphics dma remapping engine is active, we must fill gart table with dma address from dmar engine, as now graphics device access to graphics memory must go through dma remapping table to get real physical address. Add this support to all drivers which use intel_i915_insert_entries() Signed-off-by: Zhenyu Wang Signed-off-by: David Woodhouse --- drivers/char/agp/intel-agp.c | 174 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 162 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 21983456d67..20fe82b99fd 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -10,6 +10,16 @@ #include #include "agp.h" +/* + * If we have Intel graphics, we're not going to have anything other than + * an Intel IOMMU. So make the correct use of the PCI DMA API contingent + * on the Intel IOMMU support (CONFIG_DMAR). + * Only newer chipsets need to bother with this, of course. + */ +#ifdef CONFIG_DMAR +#define USE_PCI_DMA_API 1 +#endif + #define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 #define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a #define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 @@ -170,6 +180,131 @@ static struct _intel_private { int resource_valid; } intel_private; +#ifdef USE_PCI_DMA_API +static int intel_agp_map_page(void *addr, dma_addr_t *ret) +{ + *ret = pci_map_single(intel_private.pcidev, addr, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(intel_private.pcidev, *ret)) + return -EINVAL; + return 0; +} + +static void intel_agp_unmap_page(void *addr, dma_addr_t dma) +{ + pci_unmap_single(intel_private.pcidev, dma, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); +} + +static int intel_agp_map_memory(struct agp_memory *mem) +{ + struct scatterlist *sg; + int i; + + DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); + + if ((mem->page_count * sizeof(*mem->sg_list)) < 2*PAGE_SIZE) + mem->sg_list = kcalloc(mem->page_count, sizeof(*mem->sg_list), + GFP_KERNEL); + + if (mem->sg_list == NULL) { + mem->sg_list = vmalloc(mem->page_count * sizeof(*mem->sg_list)); + mem->sg_vmalloc_flag = 1; + } + + if (!mem->sg_list) { + mem->sg_vmalloc_flag = 0; + return -ENOMEM; + } + sg_init_table(mem->sg_list, mem->page_count); + + sg = mem->sg_list; + for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) + sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); + + mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, + mem->page_count, PCI_DMA_BIDIRECTIONAL); + if (!mem->num_sg) { + if (mem->sg_vmalloc_flag) + vfree(mem->sg_list); + else + kfree(mem->sg_list); + mem->sg_list = NULL; + mem->sg_vmalloc_flag = 0; + return -ENOMEM; + } + return 0; +} + +static void intel_agp_unmap_memory(struct agp_memory *mem) +{ + DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); + + pci_unmap_sg(intel_private.pcidev, mem->sg_list, + mem->page_count, PCI_DMA_BIDIRECTIONAL); + if (mem->sg_vmalloc_flag) + vfree(mem->sg_list); + else + kfree(mem->sg_list); + mem->sg_vmalloc_flag = 0; + mem->sg_list = NULL; + mem->num_sg = 0; +} + +static void intel_agp_insert_sg_entries(struct agp_memory *mem, + off_t pg_start, int mask_type) +{ + struct scatterlist *sg; + int i, j; + + j = pg_start; + + WARN_ON(!mem->num_sg); + + if (mem->num_sg == mem->page_count) { + for_each_sg(mem->sg_list, sg, mem->page_count, i) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + sg_dma_address(sg), mask_type), + intel_private.gtt+j); + j++; + } + } else { + /* sg may merge pages, but we have to seperate + * per-page addr for GTT */ + unsigned int len, m; + + for_each_sg(mem->sg_list, sg, mem->num_sg, i) { + len = sg_dma_len(sg) / PAGE_SIZE; + for (m = 0; m < len; m++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + sg_dma_address(sg) + m * PAGE_SIZE, + mask_type), + intel_private.gtt+j); + j++; + } + } + } + readl(intel_private.gtt+j-1); +} + +#else + +static void intel_agp_insert_sg_entries(struct agp_memory *mem, + off_t pg_start, int mask_type) +{ + int i, j; + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + phys_to_gart(page_to_phys(mem->pages[i])), mask_type), + intel_private.gtt+j); + } + + readl(intel_private.gtt+j-1); +} + +#endif + static int intel_i810_fetch_size(void) { u32 smram_miscc; @@ -1003,9 +1138,13 @@ static int intel_i915_configure(void) writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ +#ifndef USE_PCI_DMA_API + agp_bridge->scratch_page_dma = agp_bridge->scratch_page; +#endif + if (agp_bridge->driver->needs_scratch_page) { for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page, intel_private.gtt+i); + writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); } readl(intel_private.gtt+i-1); /* PCI Posting. */ } @@ -1038,7 +1177,7 @@ static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, int type) { - int i, j, num_entries; + int num_entries; void *temp; int ret = -EINVAL; int mask_type; @@ -1062,7 +1201,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, if ((pg_start + mem->page_count) > num_entries) goto out_err; - /* The i915 can't check the GTT for entries since its read only, + /* The i915 can't check the GTT for entries since it's read only; * depend on the caller to make the correct offset decisions. */ @@ -1078,14 +1217,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, if (!mem->is_flushed) global_cache_flush(); - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), - mask_type), - intel_private.gtt+j); - } - - readl(intel_private.gtt+j-1); + intel_agp_insert_sg_entries(mem, pg_start, mask_type); agp_bridge->driver->tlb_flush(mem); out: @@ -1110,7 +1242,7 @@ static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, } for (i = pg_start; i < (mem->page_count + pg_start); i++) - writel(agp_bridge->scratch_page, intel_private.gtt+i); + writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); readl(intel_private.gtt+i-1); @@ -2003,6 +2135,12 @@ static const struct agp_bridge_driver intel_915_driver = { .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif }; static const struct agp_bridge_driver intel_i965_driver = { @@ -2031,6 +2169,12 @@ static const struct agp_bridge_driver intel_i965_driver = { .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif }; static const struct agp_bridge_driver intel_7505_driver = { @@ -2085,6 +2229,12 @@ static const struct agp_bridge_driver intel_g33_driver = { .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif }; static int find_gmch(u16 device) -- cgit v1.2.3 From 56ec4c1e72865c6d99f643b6574e6e074c3e8823 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 27 Jul 2009 16:44:32 +0100 Subject: agp: tidy up handling of scratch pages w.r.t. DMA API Signed-off-by: David Woodhouse --- drivers/char/agp/backend.c | 23 +++++++++++++---------- drivers/char/agp/intel-agp.c | 8 ++------ 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 19ac3663acd..3c3a487f7b9 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -150,17 +150,20 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) } bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); - bridge->scratch_page = bridge->driver->mask_memory(bridge, - phys_to_gart(page_to_phys(page)), 0); - - if (bridge->driver->agp_map_page && - bridge->driver->agp_map_page(phys_to_virt(page_to_phys(page)), - &bridge->scratch_page_dma)) { - dev_err(&bridge->dev->dev, - "unable to dma-map scratch page\n"); - rc = -ENOMEM; - goto err_out_nounmap; + if (bridge->driver->agp_map_page) { + if (bridge->driver->agp_map_page(phys_to_virt(page_to_phys(page)), + &bridge->scratch_page_dma)) { + dev_err(&bridge->dev->dev, + "unable to dma-map scratch page\n"); + rc = -ENOMEM; + goto err_out_nounmap; + } + } else { + bridge->scratch_page_dma = phys_to_gart(page_to_phys(page)); } + + bridge->scratch_page = bridge->driver->mask_memory(bridge, + bridge->scratch_page_dma, 0); } size_value = bridge->driver->fetch_size(); diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 20fe82b99fd..b8f2c75b98d 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -1138,13 +1138,9 @@ static int intel_i915_configure(void) writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ -#ifndef USE_PCI_DMA_API - agp_bridge->scratch_page_dma = agp_bridge->scratch_page; -#endif - if (agp_bridge->driver->needs_scratch_page) { for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); + writel(agp_bridge->scratch_page, intel_private.gtt+i); } readl(intel_private.gtt+i-1); /* PCI Posting. */ } @@ -1242,7 +1238,7 @@ static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, } for (i = pg_start; i < (mem->page_count + pg_start); i++) - writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); + writel(agp_bridge->scratch_page, intel_private.gtt+i); readl(intel_private.gtt+i-1); -- cgit v1.2.3 From c2980d8c2961113f24863f70d8ad016f55224c81 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Jul 2009 08:39:26 +0100 Subject: agp: Switch agp_{un,}map_page() to take struct page * argument Signed-off-by: David Woodhouse --- drivers/char/agp/agp.h | 6 +++--- drivers/char/agp/backend.c | 17 ++++++++--------- drivers/char/agp/intel-agp.c | 12 ++++++------ 3 files changed, 17 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 17e6d0d3ba3..4c6e5079d87 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -122,8 +122,8 @@ struct agp_bridge_driver { int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); void (*chipset_flush)(struct agp_bridge_data *); - int (*agp_map_page)(void *addr, dma_addr_t *ret); - void (*agp_unmap_page)(void *addr, dma_addr_t dma); + int (*agp_map_page)(struct page *page, dma_addr_t *ret); + void (*agp_unmap_page)(struct page *page, dma_addr_t dma); int (*agp_map_memory)(struct agp_memory *mem); void (*agp_unmap_memory)(struct agp_memory *mem); }; @@ -139,7 +139,7 @@ struct agp_bridge_data { u32 __iomem *gatt_table; u32 *gatt_table_real; unsigned long scratch_page; - unsigned long scratch_page_real; + struct page *scratch_page_page; dma_addr_t scratch_page_dma; unsigned long gart_bus_addr; unsigned long gatt_bus_addr; diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 3c3a487f7b9..343f102090a 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -149,9 +149,9 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) return -ENOMEM; } - bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); + bridge->scratch_page_page = page; if (bridge->driver->agp_map_page) { - if (bridge->driver->agp_map_page(phys_to_virt(page_to_phys(page)), + if (bridge->driver->agp_map_page(page, &bridge->scratch_page_dma)) { dev_err(&bridge->dev->dev, "unable to dma-map scratch page\n"); @@ -205,13 +205,12 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) err_out: if (bridge->driver->needs_scratch_page && bridge->driver->agp_unmap_page) { - void *va = gart_to_virt(bridge->scratch_page_real); - - bridge->driver->agp_unmap_page(va, bridge->scratch_page_dma); + bridge->driver->agp_unmap_page(bridge->scratch_page_page, + bridge->scratch_page_dma); } err_out_nounmap: if (bridge->driver->needs_scratch_page) { - void *va = gart_to_virt(bridge->scratch_page_real); + void *va = page_address(bridge->scratch_page_page); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); @@ -238,11 +237,11 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) if (bridge->driver->agp_destroy_page && bridge->driver->needs_scratch_page) { - void *va = gart_to_virt(bridge->scratch_page_real); + void *va = page_address(bridge->scratch_page_page); if (bridge->driver->agp_unmap_page) - bridge->driver->agp_unmap_page(va, - bridge->scratch_page_dma); + bridge->driver->agp_unmap_page(bridge->scratch_page_page, + bridge->scratch_page_dma); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index b8f2c75b98d..148d7e38fdd 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -181,19 +181,19 @@ static struct _intel_private { } intel_private; #ifdef USE_PCI_DMA_API -static int intel_agp_map_page(void *addr, dma_addr_t *ret) +static int intel_agp_map_page(struct page *page, dma_addr_t *ret) { - *ret = pci_map_single(intel_private.pcidev, addr, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + *ret = pci_map_page(intel_private.pcidev, page, 0, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(intel_private.pcidev, *ret)) return -EINVAL; return 0; } -static void intel_agp_unmap_page(void *addr, dma_addr_t dma) +static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) { - pci_unmap_single(intel_private.pcidev, dma, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + pci_unmap_page(intel_private.pcidev, dma, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); } static int intel_agp_map_memory(struct agp_memory *mem) -- cgit v1.2.3 From 91b8e3056bf9107b688eb076c9b804171364db71 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Jul 2009 08:49:12 +0100 Subject: intel-agp: Move repeated sglist free into separate function Signed-off-by: David Woodhouse --- drivers/char/agp/intel-agp.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 148d7e38fdd..b9d9886ff3c 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -196,6 +196,18 @@ static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); } +static void intel_agp_free_sglist(struct agp_memory *mem) +{ + + if (mem->sg_vmalloc_flag) + vfree(mem->sg_list); + else + kfree(mem->sg_list); + mem->sg_vmalloc_flag = 0; + mem->sg_list = NULL; + mem->num_sg = 0; +} + static int intel_agp_map_memory(struct agp_memory *mem) { struct scatterlist *sg; @@ -224,13 +236,8 @@ static int intel_agp_map_memory(struct agp_memory *mem) mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, mem->page_count, PCI_DMA_BIDIRECTIONAL); - if (!mem->num_sg) { - if (mem->sg_vmalloc_flag) - vfree(mem->sg_list); - else - kfree(mem->sg_list); - mem->sg_list = NULL; - mem->sg_vmalloc_flag = 0; + if (unlikely(!mem->num_sg)) { + intel_agp_free_sglist(mem); return -ENOMEM; } return 0; @@ -242,13 +249,7 @@ static void intel_agp_unmap_memory(struct agp_memory *mem) pci_unmap_sg(intel_private.pcidev, mem->sg_list, mem->page_count, PCI_DMA_BIDIRECTIONAL); - if (mem->sg_vmalloc_flag) - vfree(mem->sg_list); - else - kfree(mem->sg_list); - mem->sg_vmalloc_flag = 0; - mem->sg_list = NULL; - mem->num_sg = 0; + intel_agp_free_sglist(mem); } static void intel_agp_insert_sg_entries(struct agp_memory *mem, -- cgit v1.2.3 From f692775d7e0a22477143cd884e45c955448ac7d2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Jul 2009 09:28:45 +0100 Subject: intel-agp: fix sglist allocation to avoid vmalloc() Signed-off-by: David Woodhouse --- drivers/char/agp/intel-agp.c | 29 ++++++++++------------------- include/linux/agp_backend.h | 1 - 2 files changed, 10 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index b9d9886ff3c..d8c80d8be5e 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -198,39 +198,30 @@ static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) static void intel_agp_free_sglist(struct agp_memory *mem) { + struct sg_table st; + + st.sgl = mem->sg_list; + st.orig_nents = st.nents = mem->page_count; + + sg_free_table(&st); - if (mem->sg_vmalloc_flag) - vfree(mem->sg_list); - else - kfree(mem->sg_list); - mem->sg_vmalloc_flag = 0; mem->sg_list = NULL; mem->num_sg = 0; } static int intel_agp_map_memory(struct agp_memory *mem) { + struct sg_table st; struct scatterlist *sg; int i; DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); - if ((mem->page_count * sizeof(*mem->sg_list)) < 2*PAGE_SIZE) - mem->sg_list = kcalloc(mem->page_count, sizeof(*mem->sg_list), - GFP_KERNEL); - - if (mem->sg_list == NULL) { - mem->sg_list = vmalloc(mem->page_count * sizeof(*mem->sg_list)); - mem->sg_vmalloc_flag = 1; - } - - if (!mem->sg_list) { - mem->sg_vmalloc_flag = 0; + if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL)) return -ENOMEM; - } - sg_init_table(mem->sg_list, mem->page_count); - sg = mem->sg_list; + mem->sg_list = sg = st.sgl; + for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index 8a294d65b9b..880130f7311 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -80,7 +80,6 @@ struct agp_memory { bool is_bound; bool is_flushed; bool vmalloc_flag; - bool sg_vmalloc_flag; /* list of agp_memory mapped to the aperture */ struct list_head mapped_list; /* DMA-mapped addresses */ -- cgit v1.2.3 From 6a12235c7d2d75c7d94b9afcaaecd422ff845ce0 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Jul 2009 10:25:58 +0100 Subject: agp: kill phys_to_gart() and gart_to_phys() There seems to be no reason for these -- they're a 1:1 mapping on all platforms. Signed-off-by: David Woodhouse --- arch/alpha/include/asm/agp.h | 4 ---- arch/ia64/include/asm/agp.h | 4 ---- arch/parisc/include/asm/agp.h | 4 ---- arch/powerpc/include/asm/agp.h | 4 ---- arch/sparc/include/asm/agp.h | 4 ---- arch/x86/include/asm/agp.h | 4 ---- drivers/char/agp/agp.h | 3 --- drivers/char/agp/ali-agp.c | 4 ++-- drivers/char/agp/amd-k7-agp.c | 8 ++++---- drivers/char/agp/amd64-agp.c | 6 +++--- drivers/char/agp/ati-agp.c | 6 +++--- drivers/char/agp/backend.c | 2 +- drivers/char/agp/efficeon-agp.c | 4 ++-- drivers/char/agp/generic.c | 6 +++--- drivers/char/agp/hp-agp.c | 4 ++-- drivers/char/agp/i460-agp.c | 4 ++-- drivers/char/agp/intel-agp.c | 7 +++---- drivers/char/agp/nvidia-agp.c | 2 +- drivers/char/agp/sgi-agp.c | 2 +- drivers/char/agp/sworks-agp.c | 8 ++++---- drivers/char/agp/uninorth-agp.c | 2 +- 21 files changed, 32 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/arch/alpha/include/asm/agp.h b/arch/alpha/include/asm/agp.h index 26c17913529..a94d48b8677 100644 --- a/arch/alpha/include/asm/agp.h +++ b/arch/alpha/include/asm/agp.h @@ -9,10 +9,6 @@ #define unmap_page_from_agp(page) #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/ia64/include/asm/agp.h b/arch/ia64/include/asm/agp.h index c11fdd8ab4d..01d09c401c5 100644 --- a/arch/ia64/include/asm/agp.h +++ b/arch/ia64/include/asm/agp.h @@ -17,10 +17,6 @@ #define unmap_page_from_agp(page) /* nothing */ #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/parisc/include/asm/agp.h b/arch/parisc/include/asm/agp.h index 9651660da63..d226ffa8fc1 100644 --- a/arch/parisc/include/asm/agp.h +++ b/arch/parisc/include/asm/agp.h @@ -11,10 +11,6 @@ #define unmap_page_from_agp(page) /* nothing */ #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/powerpc/include/asm/agp.h b/arch/powerpc/include/asm/agp.h index 86455c4c31e..416e12c2d50 100644 --- a/arch/powerpc/include/asm/agp.h +++ b/arch/powerpc/include/asm/agp.h @@ -8,10 +8,6 @@ #define unmap_page_from_agp(page) #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/sparc/include/asm/agp.h b/arch/sparc/include/asm/agp.h index c2456870b05..70f52c1661b 100644 --- a/arch/sparc/include/asm/agp.h +++ b/arch/sparc/include/asm/agp.h @@ -7,10 +7,6 @@ #define unmap_page_from_agp(page) #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/x86/include/asm/agp.h b/arch/x86/include/asm/agp.h index 9825cd64c9b..eec2a70d437 100644 --- a/arch/x86/include/asm/agp.h +++ b/arch/x86/include/asm/agp.h @@ -22,10 +22,6 @@ */ #define flush_agp_cache() wbinvd() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 4c6e5079d87..d6f36c004d9 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -318,9 +318,6 @@ void agp3_generic_cleanup(void); #define AGP_GENERIC_SIZES_ENTRIES 11 extern const struct aper_size_info_16 agp3_generic_sizes[]; -#define virt_to_gart(x) (phys_to_gart(virt_to_phys(x))) -#define gart_to_virt(x) (phys_to_virt(gart_to_phys(x))) - extern int agp_off; extern int agp_try_unsupported_boot; diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index 201ef3ffd48..d2ce68f27e4 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c @@ -152,7 +152,7 @@ static struct page *m1541_alloc_page(struct agp_bridge_data *bridge) pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - phys_to_gart(page_to_phys(page))) | ALI_CACHE_FLUSH_EN )); + page_to_phys(page)) | ALI_CACHE_FLUSH_EN )); return page; } @@ -180,7 +180,7 @@ static void m1541_destroy_page(struct page *page, int flags) pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - phys_to_gart(page_to_phys(page))) | ALI_CACHE_FLUSH_EN)); + page_to_phys(page)) | ALI_CACHE_FLUSH_EN)); } agp_generic_destroy_page(page, flags); } diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index 542a87895ae..73dbf40c874 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -44,7 +44,7 @@ static int amd_create_page_map(struct amd_page_map *page_map) #ifndef CONFIG_X86 SetPageReserved(virt_to_page(page_map->real)); global_cache_flush(); - page_map->remapped = ioremap_nocache(virt_to_gart(page_map->real), + page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), PAGE_SIZE); if (page_map->remapped == NULL) { ClearPageReserved(virt_to_page(page_map->real)); @@ -160,7 +160,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Get the address for the gart region. * This is a bus address even on the alpha, b/c its @@ -173,7 +173,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { - writel(virt_to_gart(amd_irongate_private.gatt_pages[i]->real) | 1, + writel(virt_to_phys(amd_irongate_private.gatt_pages[i]->real) | 1, page_dir.remapped+GET_PAGE_DIR_OFF(addr)); readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } @@ -325,7 +325,7 @@ static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type) addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_generic_mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mem->type), cur_gatt+GET_GATT_OFF(addr)); readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index e85a5b3e952..2fb2e6cc322 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -79,7 +79,7 @@ static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type) for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { tmp = agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mask_type); BUG_ON(tmp & 0xffffff0000000ffcULL); @@ -178,7 +178,7 @@ static const struct aper_size_info_32 amd_8151_sizes[7] = static int amd_8151_configure(void) { - unsigned long gatt_bus = virt_to_gart(agp_bridge->gatt_table_real); + unsigned long gatt_bus = virt_to_phys(agp_bridge->gatt_table_real); int i; /* Configure AGP regs in each x86-64 host bridge. */ @@ -558,7 +558,7 @@ static void __devexit agp_amd64_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); - release_mem_region(virt_to_gart(bridge->gatt_table_real), + release_mem_region(virt_to_phys(bridge->gatt_table_real), amd64_aperture_sizes[bridge->aperture_size_idx].size); agp_remove_bridge(bridge); agp_put_bridge(bridge); diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 59ebd60c1b6..3b2ecbe86eb 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -302,7 +302,7 @@ static int ati_insert_memory(struct agp_memory * mem, addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mem->type), cur_gatt+GET_GATT_OFF(addr)); } @@ -360,7 +360,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *) page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Write out the size register */ current_size = A_SIZE_LVL2(agp_bridge->current_size); @@ -390,7 +390,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { - writel(virt_to_gart(ati_generic_private.gatt_pages[i]->real) | 1, + writel(virt_to_phys(ati_generic_private.gatt_pages[i]->real) | 1, page_dir.remapped+GET_PAGE_DIR_OFF(addr)); readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 343f102090a..ad87753f6de 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -159,7 +159,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) goto err_out_nounmap; } } else { - bridge->scratch_page_dma = phys_to_gart(page_to_phys(page)); + bridge->scratch_page_dma = page_to_phys(page); } bridge->scratch_page = bridge->driver->mask_memory(bridge, diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 35d50f2861b..793f39ea961 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -67,7 +67,7 @@ static const struct gatt_mask efficeon_generic_masks[] = /* This function does the same thing as mask_memory() for this chipset... */ static inline unsigned long efficeon_mask_memory(struct page *page) { - unsigned long addr = phys_to_gart(page_to_phys(page)); + unsigned long addr = page_to_phys(page); return addr | 0x00000001; } @@ -226,7 +226,7 @@ static int efficeon_create_gatt_table(struct agp_bridge_data *bridge) efficeon_private.l1_table[index] = page; - value = virt_to_gart((unsigned long *)page) | pati | present | index; + value = virt_to_phys((unsigned long *)page) | pati | present | index; pci_write_config_dword(agp_bridge->dev, EFFICEON_ATTPAGE, value); diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 28f0208c66a..c50543966eb 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -988,7 +988,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) set_memory_uc((unsigned long)table, 1 << page_order); bridge->gatt_table = (void *)table; #else - bridge->gatt_table = ioremap_nocache(virt_to_gart(table), + bridge->gatt_table = ioremap_nocache(virt_to_phys(table), (PAGE_SIZE * (1 << page_order))); bridge->driver->cache_flush(); #endif @@ -1001,7 +1001,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) return -ENOMEM; } - bridge->gatt_bus_addr = virt_to_gart(bridge->gatt_table_real); + bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real); /* AK: bogus, should encode addresses > 4GB */ for (i = 0; i < num_entries; i++) { @@ -1142,7 +1142,7 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(bridge->driver->mask_memory(bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mask_type), bridge->gatt_table+j); } diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 64dbf4b1cf2..501e293e5ad 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -107,7 +107,7 @@ static int __init hp_zx1_ioc_shared(void) hp->gart_size = HP_ZX1_GART_SIZE; hp->gatt_entries = hp->gart_size / hp->io_page_size; - hp->io_pdir = gart_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE)); + hp->io_pdir = phys_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE)); hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { @@ -246,7 +246,7 @@ hp_zx1_configure (void) agp_bridge->mode = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS); if (hp->io_pdir_owner) { - writel(virt_to_gart(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE); + writel(virt_to_phys(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE); readl(hp->ioc_regs+HP_ZX1_PDIR_BASE); writel(hp->io_tlb_ps, hp->ioc_regs+HP_ZX1_TCNFG); readl(hp->ioc_regs+HP_ZX1_TCNFG); diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 54191f86053..e763d3312ce 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -325,7 +325,7 @@ static int i460_insert_memory_small_io_page (struct agp_memory *mem, io_page_size = 1UL << I460_IO_PAGE_SHIFT; for (i = 0, j = io_pg_start; i < mem->page_count; i++) { - paddr = phys_to_gart(page_to_phys(mem->pages[i])); + paddr = page_to_phys(mem->pages[i]); for (k = 0; k < I460_IOPAGES_PER_KPAGE; k++, j++, paddr += io_page_size) WR_GATT(j, i460_mask_memory(agp_bridge, paddr, mem->type)); } @@ -382,7 +382,7 @@ static int i460_alloc_large_page (struct lp_desc *lp) return -ENOMEM; } - lp->paddr = phys_to_gart(page_to_phys(lp->page)); + lp->paddr = page_to_phys(lp->page); lp->refcount = 0; atomic_add(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp); return 0; diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index d8c80d8be5e..aa8889e8afc 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -288,7 +288,7 @@ static void intel_agp_insert_sg_entries(struct agp_memory *mem, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), mask_type), + page_to_phys(mem->pages[i]), mask_type), intel_private.gtt+j); } @@ -470,8 +470,7 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, global_cache_flush(); for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), - mask_type), + page_to_phys(mem->pages[i]), mask_type), intel_private.registers+I810_PTE_BASE+(j*4)); } readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); @@ -977,7 +976,7 @@ static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), mask_type), + page_to_phys(mem->pages[i]), mask_type), intel_private.registers+I810_PTE_BASE+(j*4)); } readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index cedacee30ec..7e36d2b4f9d 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -225,7 +225,7 @@ static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), mask_type), + page_to_phys(mem->pages[i]), mask_type), agp_bridge->gatt_table+nvidia_private.pg_offset+j); } diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index 0d47fa84740..0d426ae39c8 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -190,7 +190,7 @@ static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { table[j] = bridge->driver->mask_memory(bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mem->type); } diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index 07259952fc3..13acaaf64ed 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -155,7 +155,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) /* Create a fake scratch directory */ for (i = 0; i < 1024; i++) { writel(agp_bridge->scratch_page, serverworks_private.scratch_dir.remapped+i); - writel(virt_to_gart(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i); + writel(virt_to_phys(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i); } retval = serverworks_create_gatt_pages(value->num_entries / 1024); @@ -167,7 +167,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Get the address for the gart region. * This is a bus address even on the alpha, b/c its @@ -179,7 +179,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++) - writel(virt_to_gart(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i); + writel(virt_to_phys(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i); return 0; } @@ -350,7 +350,7 @@ static int serverworks_insert_memory(struct agp_memory *mem, addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = SVRWRKS_GET_GATT(addr); writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), mem->type), + page_to_phys(mem->pages[i]), mem->type), cur_gatt+GET_GATT_OFF(addr)); } serverworks_tlbflush(mem); diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index f192c3b9ad4..2e993112ab8 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -431,7 +431,7 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) bridge->gatt_table_real = (u32 *) table; bridge->gatt_table = (u32 *)table; - bridge->gatt_bus_addr = virt_to_gart(table); + bridge->gatt_bus_addr = virt_to_phys(table); for (i = 0; i < num_entries; i++) bridge->gatt_table[i] = 0; -- cgit v1.2.3 From ba3139f2577eee24479db73b8dfc7d78eaf4c486 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 5 Aug 2009 08:12:40 +0100 Subject: intel-agp: Set dma mask for i915 If DMAR is configured in but absent, we really do want to make sure that the dma mask is set appropriately. Otherwise we get mapping failures on highmem. Spotted by Zhenyu Wang. Signed-off-by: David Woodhouse --- drivers/char/agp/intel-agp.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index aa8889e8afc..9bc3a0b82b9 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -1140,6 +1140,12 @@ static int intel_i915_configure(void) intel_i9xx_setup_flush(); +#ifdef USE_PCI_DMA_API + if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(36))) + dev_err(&intel_private.pcidev->dev, + "set gfx device dma mask 36bit failed!\n"); +#endif + return 0; } -- cgit v1.2.3 From 5e8d6b8bf94f1ffcb7e3c31b73284c20f297f191 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 6 Aug 2009 20:20:43 +1000 Subject: agp: fix uninorth build Signed-off-by: Dave Airlie --- drivers/char/agp/uninorth-agp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 2e993112ab8..4317a5588da 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -135,7 +135,7 @@ static int uninorth_configure(void) if (is_u3) { pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_DUMMY_PAGE, - agp_bridge->scratch_page_real >> 12); + page_to_phys(agp_bridge->scratch_page_page) >> 12); } return 0; -- cgit v1.2.3 From 121264827656f5f06328b17983c796af17dc5949 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Mon, 14 Sep 2009 10:47:06 +0800 Subject: agp/intel: remove restore in resume As early pci resume has already restored config for host bridge and graphics device, don't need to restore it again, This removes an original order hack for graphics device restore. This fixed the resume hang issue found by Alan Stern on 845G, caused by extra config restore on graphics device. Cc: Stable Team Cc: Alan Stern Signed-off-by: Zhenyu Wang Signed-off-by: Dave Airlie --- drivers/char/agp/intel-agp.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 9bc3a0b82b9..5eeaeeeaa2c 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -2451,15 +2451,6 @@ static int agp_intel_resume(struct pci_dev *pdev) struct agp_bridge_data *bridge = pci_get_drvdata(pdev); int ret_val; - pci_restore_state(pdev); - - /* We should restore our graphics device's config space, - * as host bridge (00:00) resumes before graphics device (02:00), - * then our access to its pci space can work right. - */ - if (intel_private.pcidev) - pci_restore_state(intel_private.pcidev); - if (bridge->driver == &intel_generic_driver) intel_configure(); else if (bridge->driver == &intel_850_driver) -- cgit v1.2.3