[RFC][PATCH 3/4][AGP] intel_agp: add support for graphics dma remapping on G33

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[PATCH] [AGP] intel_agp: add support for graphics dma remapping on G33

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 support on G33 chipset, which has graphics device specific
dmar engine avaiable.

Signed-off-by: Zhenyu Wang <[email protected]>
---
 drivers/char/agp/Kconfig     |   12 +++-
 drivers/char/agp/intel-agp.c |  134 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 141 insertions(+), 5 deletions(-)

diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig
index ccb1fa8..e60c2b0 100644
--- a/drivers/char/agp/Kconfig
+++ b/drivers/char/agp/Kconfig
@@ -74,8 +74,16 @@ config AGP_INTEL
 	  on Intel 440LX/BX/GX, 815, 820, 830, 840, 845, 850, 860, 875,
 	  E7205 and E7505 chipsets and full support for the 810, 815, 830M,
 	  845G, 852GM, 855GM, 865G and I915 integrated graphics chipsets.
-
-
+	
+config AGP_INTEL_DMAR
+	bool
+	depends on AGP_INTEL && DMAR && !DMAR_GFX_WA
+	default y
+	help
+	  This option will enable graphics address remapping with Intel
+	  DMAR engine aka VT-d if you don't select graphics workaround on
+	  Intel DMAR. The graphics address filled to gart table will be
+	  changed by remapping within graphics DMAR engine for domain.
 
 config AGP_NVIDIA
 	tristate "NVIDIA nForce/nForce2 chipset support"
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index d879619..7424667 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -9,6 +9,9 @@
 #include <linux/pagemap.h>
 #include <linux/agp_backend.h>
 #include "agp.h"
+#ifdef CONFIG_AGP_INTEL_DMAR
+#include <linux/dmar.h>
+#endif
 
 #define PCI_DEVICE_ID_INTEL_82946GZ_HB      0x2970
 #define PCI_DEVICE_ID_INTEL_82946GZ_IG      0x2972
@@ -115,6 +118,97 @@ static struct _intel_private {
 	int gtt_entries;			/* i830+ */
 } intel_private;
 
+static int intel_agp_require_remapping(void)
+{
+#ifdef CONFIG_AGP_INTEL_DMAR
+	return intel_iommu_gfx_remapping();
+#else
+	return 0;
+#endif
+}
+
+#ifdef CONFIG_AGP_INTEL_DMAR
+static int intel_agp_map_pages(struct agp_memory *mem, void *virt, dma_addr_t *ret, int is_single)
+{
+	int i;
+	struct scatterlist *sg;
+
+	if (is_single) {
+		*ret = pci_map_single(intel_private.pcidev, virt, PAGE_SIZE,
+				PCI_DMA_BIDIRECTIONAL);
+		if (pci_dma_mapping_error(*ret))
+			return -EINVAL;
+		return 0;
+	}
+
+	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_buf(sg, gart_to_virt(mem->memory[i]), PAGE_SIZE);
+
+	mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list,
+					mem->page_count, PCI_DMA_BIDIRECTIONAL);
+	if (mem->num_sg == 0) {
+		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;
+	}
+	mem->is_mapped = TRUE;
+	return 0;
+}
+
+static void intel_agp_unmap_pages(struct agp_memory *mem, dma_addr_t addr, int is_single)
+{
+	if (is_single) {
+		pci_unmap_single(intel_private.pcidev, addr, PAGE_SIZE,
+				PCI_DMA_BIDIRECTIONAL);
+		return;
+	}
+	DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count);
+
+	if (!mem->is_mapped)
+		return;
+
+	if (mem->num_sg)
+		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;
+}
+#else
+static int intel_agp_map_pages(struct agp_memory *mem, void *virt, dma_addr_t *ret, int is_single)
+{
+    return 0;
+}
+static void intel_agp_unmap_pages(struct agp_memory *mem, dma_addr_t addr, int is_single)
+{
+}
+#endif
+
 static int intel_i810_fetch_size(void)
 {
 	u32 smram_miscc;
@@ -847,9 +941,40 @@ 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,
-			mem->memory[i], mask_type), intel_private.gtt+j);
+	/* map mem if current graphics dmar engine is active
+	 * and workaround is not applied. */
+	if (mem->is_mapped) {
+		struct scatterlist *sg;
+
+		j = pg_start;
+		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++;
+				}
+			}
+		}
+	} else {
+		for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+			writel(agp_bridge->driver->mask_memory(agp_bridge,
+						mem->memory[i], mask_type),
+					intel_private.gtt+j);
+		}
 	}
 
 	readl(intel_private.gtt+j-1);
@@ -1796,6 +1921,9 @@ static const struct agp_bridge_driver intel_g33_driver = {
 	.agp_alloc_page         = agp_generic_alloc_page,
 	.agp_destroy_page       = agp_generic_destroy_page,
 	.agp_type_to_mask_type  = intel_i830_type_to_mask_type,
+	.agp_require_remapping  = intel_agp_require_remapping,
+	.agp_map_page		= intel_agp_map_pages,
+	.agp_unmap_page		= intel_agp_unmap_pages,
 };
 
 static int find_gmch(u16 device)
-- 
1.5.3.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux