diff options
-rw-r--r-- | drivers/char/agp/intel-agp.c | 174 |
1 files changed, 162 insertions, 12 deletions
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 21983456d672..20fe82b99fdb 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c | |||
@@ -10,6 +10,16 @@ | |||
10 | #include <linux/agp_backend.h> | 10 | #include <linux/agp_backend.h> |
11 | #include "agp.h" | 11 | #include "agp.h" |
12 | 12 | ||
13 | /* | ||
14 | * If we have Intel graphics, we're not going to have anything other than | ||
15 | * an Intel IOMMU. So make the correct use of the PCI DMA API contingent | ||
16 | * on the Intel IOMMU support (CONFIG_DMAR). | ||
17 | * Only newer chipsets need to bother with this, of course. | ||
18 | */ | ||
19 | #ifdef CONFIG_DMAR | ||
20 | #define USE_PCI_DMA_API 1 | ||
21 | #endif | ||
22 | |||
13 | #define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 | 23 | #define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 |
14 | #define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a | 24 | #define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a |
15 | #define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 | 25 | #define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 |
@@ -170,6 +180,131 @@ static struct _intel_private { | |||
170 | int resource_valid; | 180 | int resource_valid; |
171 | } intel_private; | 181 | } intel_private; |
172 | 182 | ||
183 | #ifdef USE_PCI_DMA_API | ||
184 | static int intel_agp_map_page(void *addr, dma_addr_t *ret) | ||
185 | { | ||
186 | *ret = pci_map_single(intel_private.pcidev, addr, | ||
187 | PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | ||
188 | if (pci_dma_mapping_error(intel_private.pcidev, *ret)) | ||
189 | return -EINVAL; | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static void intel_agp_unmap_page(void *addr, dma_addr_t dma) | ||
194 | { | ||
195 | pci_unmap_single(intel_private.pcidev, dma, | ||
196 | PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | ||
197 | } | ||
198 | |||
199 | static int intel_agp_map_memory(struct agp_memory *mem) | ||
200 | { | ||
201 | struct scatterlist *sg; | ||
202 | int i; | ||
203 | |||
204 | DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); | ||
205 | |||
206 | if ((mem->page_count * sizeof(*mem->sg_list)) < 2*PAGE_SIZE) | ||
207 | mem->sg_list = kcalloc(mem->page_count, sizeof(*mem->sg_list), | ||
208 | GFP_KERNEL); | ||
209 | |||
210 | if (mem->sg_list == NULL) { | ||
211 | mem->sg_list = vmalloc(mem->page_count * sizeof(*mem->sg_list)); | ||
212 | mem->sg_vmalloc_flag = 1; | ||
213 | } | ||
214 | |||
215 | if (!mem->sg_list) { | ||
216 | mem->sg_vmalloc_flag = 0; | ||
217 | return -ENOMEM; | ||
218 | } | ||
219 | sg_init_table(mem->sg_list, mem->page_count); | ||
220 | |||
221 | sg = mem->sg_list; | ||
222 | for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) | ||
223 | sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); | ||
224 | |||
225 | mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, | ||
226 | mem->page_count, PCI_DMA_BIDIRECTIONAL); | ||
227 | if (!mem->num_sg) { | ||
228 | if (mem->sg_vmalloc_flag) | ||
229 | vfree(mem->sg_list); | ||
230 | else | ||
231 | kfree(mem->sg_list); | ||
232 | mem->sg_list = NULL; | ||
233 | mem->sg_vmalloc_flag = 0; | ||
234 | return -ENOMEM; | ||
235 | } | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static void intel_agp_unmap_memory(struct agp_memory *mem) | ||
240 | { | ||
241 | DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); | ||
242 | |||
243 | pci_unmap_sg(intel_private.pcidev, mem->sg_list, | ||
244 | mem->page_count, PCI_DMA_BIDIRECTIONAL); | ||
245 | if (mem->sg_vmalloc_flag) | ||
246 | vfree(mem->sg_list); | ||
247 | else | ||
248 | kfree(mem->sg_list); | ||
249 | mem->sg_vmalloc_flag = 0; | ||
250 | mem->sg_list = NULL; | ||
251 | mem->num_sg = 0; | ||
252 | } | ||
253 | |||
254 | static void intel_agp_insert_sg_entries(struct agp_memory *mem, | ||
255 | off_t pg_start, int mask_type) | ||
256 | { | ||
257 | struct scatterlist *sg; | ||
258 | int i, j; | ||
259 | |||
260 | j = pg_start; | ||
261 | |||
262 | WARN_ON(!mem->num_sg); | ||
263 | |||
264 | if (mem->num_sg == mem->page_count) { | ||
265 | for_each_sg(mem->sg_list, sg, mem->page_count, i) { | ||
266 | writel(agp_bridge->driver->mask_memory(agp_bridge, | ||
267 | sg_dma_address(sg), mask_type), | ||
268 | intel_private.gtt+j); | ||
269 | j++; | ||
270 | } | ||
271 | } else { | ||
272 | /* sg may merge pages, but we have to seperate | ||
273 | * per-page addr for GTT */ | ||
274 | unsigned int len, m; | ||
275 | |||
276 | for_each_sg(mem->sg_list, sg, mem->num_sg, i) { | ||
277 | len = sg_dma_len(sg) / PAGE_SIZE; | ||
278 | for (m = 0; m < len; m++) { | ||
279 | writel(agp_bridge->driver->mask_memory(agp_bridge, | ||
280 | sg_dma_address(sg) + m * PAGE_SIZE, | ||
281 | mask_type), | ||
282 | intel_private.gtt+j); | ||
283 | j++; | ||
284 | } | ||
285 | } | ||
286 | } | ||
287 | readl(intel_private.gtt+j-1); | ||
288 | } | ||
289 | |||
290 | #else | ||
291 | |||
292 | static void intel_agp_insert_sg_entries(struct agp_memory *mem, | ||
293 | off_t pg_start, int mask_type) | ||
294 | { | ||
295 | int i, j; | ||
296 | |||
297 | for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { | ||
298 | writel(agp_bridge->driver->mask_memory(agp_bridge, | ||
299 | phys_to_gart(page_to_phys(mem->pages[i])), mask_type), | ||
300 | intel_private.gtt+j); | ||
301 | } | ||
302 | |||
303 | readl(intel_private.gtt+j-1); | ||
304 | } | ||
305 | |||
306 | #endif | ||
307 | |||
173 | static int intel_i810_fetch_size(void) | 308 | static int intel_i810_fetch_size(void) |
174 | { | 309 | { |
175 | u32 smram_miscc; | 310 | u32 smram_miscc; |
@@ -1003,9 +1138,13 @@ static int intel_i915_configure(void) | |||
1003 | writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); | 1138 | writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); |
1004 | readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ | 1139 | readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ |
1005 | 1140 | ||
1141 | #ifndef USE_PCI_DMA_API | ||
1142 | agp_bridge->scratch_page_dma = agp_bridge->scratch_page; | ||
1143 | #endif | ||
1144 | |||
1006 | if (agp_bridge->driver->needs_scratch_page) { | 1145 | if (agp_bridge->driver->needs_scratch_page) { |
1007 | for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { | 1146 | for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { |
1008 | writel(agp_bridge->scratch_page, intel_private.gtt+i); | 1147 | writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); |
1009 | } | 1148 | } |
1010 | readl(intel_private.gtt+i-1); /* PCI Posting. */ | 1149 | readl(intel_private.gtt+i-1); /* PCI Posting. */ |
1011 | } | 1150 | } |
@@ -1038,7 +1177,7 @@ static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) | |||
1038 | static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, | 1177 | static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, |
1039 | int type) | 1178 | int type) |
1040 | { | 1179 | { |
1041 | int i, j, num_entries; | 1180 | int num_entries; |
1042 | void *temp; | 1181 | void *temp; |
1043 | int ret = -EINVAL; | 1182 | int ret = -EINVAL; |
1044 | int mask_type; | 1183 | int mask_type; |
@@ -1062,7 +1201,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, | |||
1062 | if ((pg_start + mem->page_count) > num_entries) | 1201 | if ((pg_start + mem->page_count) > num_entries) |
1063 | goto out_err; | 1202 | goto out_err; |
1064 | 1203 | ||
1065 | /* The i915 can't check the GTT for entries since its read only, | 1204 | /* The i915 can't check the GTT for entries since it's read only; |
1066 | * depend on the caller to make the correct offset decisions. | 1205 | * depend on the caller to make the correct offset decisions. |
1067 | */ | 1206 | */ |
1068 | 1207 | ||
@@ -1078,14 +1217,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, | |||
1078 | if (!mem->is_flushed) | 1217 | if (!mem->is_flushed) |
1079 | global_cache_flush(); | 1218 | global_cache_flush(); |
1080 | 1219 | ||
1081 | for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { | 1220 | intel_agp_insert_sg_entries(mem, pg_start, mask_type); |
1082 | writel(agp_bridge->driver->mask_memory(agp_bridge, | ||
1083 | phys_to_gart(page_to_phys(mem->pages[i])), | ||
1084 | mask_type), | ||
1085 | intel_private.gtt+j); | ||
1086 | } | ||
1087 | |||
1088 | readl(intel_private.gtt+j-1); | ||
1089 | agp_bridge->driver->tlb_flush(mem); | 1221 | agp_bridge->driver->tlb_flush(mem); |
1090 | 1222 | ||
1091 | out: | 1223 | out: |
@@ -1110,7 +1242,7 @@ static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, | |||
1110 | } | 1242 | } |
1111 | 1243 | ||
1112 | for (i = pg_start; i < (mem->page_count + pg_start); i++) | 1244 | for (i = pg_start; i < (mem->page_count + pg_start); i++) |
1113 | writel(agp_bridge->scratch_page, intel_private.gtt+i); | 1245 | writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); |
1114 | 1246 | ||
1115 | readl(intel_private.gtt+i-1); | 1247 | readl(intel_private.gtt+i-1); |
1116 | 1248 | ||
@@ -2003,6 +2135,12 @@ static const struct agp_bridge_driver intel_915_driver = { | |||
2003 | .agp_destroy_pages = agp_generic_destroy_pages, | 2135 | .agp_destroy_pages = agp_generic_destroy_pages, |
2004 | .agp_type_to_mask_type = intel_i830_type_to_mask_type, | 2136 | .agp_type_to_mask_type = intel_i830_type_to_mask_type, |
2005 | .chipset_flush = intel_i915_chipset_flush, | 2137 | .chipset_flush = intel_i915_chipset_flush, |
2138 | #ifdef USE_PCI_DMA_API | ||
2139 | .agp_map_page = intel_agp_map_page, | ||
2140 | .agp_unmap_page = intel_agp_unmap_page, | ||
2141 | .agp_map_memory = intel_agp_map_memory, | ||
2142 | .agp_unmap_memory = intel_agp_unmap_memory, | ||
2143 | #endif | ||
2006 | }; | 2144 | }; |
2007 | 2145 | ||
2008 | static const struct agp_bridge_driver intel_i965_driver = { | 2146 | static const struct agp_bridge_driver intel_i965_driver = { |
@@ -2031,6 +2169,12 @@ static const struct agp_bridge_driver intel_i965_driver = { | |||
2031 | .agp_destroy_pages = agp_generic_destroy_pages, | 2169 | .agp_destroy_pages = agp_generic_destroy_pages, |
2032 | .agp_type_to_mask_type = intel_i830_type_to_mask_type, | 2170 | .agp_type_to_mask_type = intel_i830_type_to_mask_type, |
2033 | .chipset_flush = intel_i915_chipset_flush, | 2171 | .chipset_flush = intel_i915_chipset_flush, |
2172 | #ifdef USE_PCI_DMA_API | ||
2173 | .agp_map_page = intel_agp_map_page, | ||
2174 | .agp_unmap_page = intel_agp_unmap_page, | ||
2175 | .agp_map_memory = intel_agp_map_memory, | ||
2176 | .agp_unmap_memory = intel_agp_unmap_memory, | ||
2177 | #endif | ||
2034 | }; | 2178 | }; |
2035 | 2179 | ||
2036 | static const struct agp_bridge_driver intel_7505_driver = { | 2180 | static const struct agp_bridge_driver intel_7505_driver = { |
@@ -2085,6 +2229,12 @@ static const struct agp_bridge_driver intel_g33_driver = { | |||
2085 | .agp_destroy_pages = agp_generic_destroy_pages, | 2229 | .agp_destroy_pages = agp_generic_destroy_pages, |
2086 | .agp_type_to_mask_type = intel_i830_type_to_mask_type, | 2230 | .agp_type_to_mask_type = intel_i830_type_to_mask_type, |
2087 | .chipset_flush = intel_i915_chipset_flush, | 2231 | .chipset_flush = intel_i915_chipset_flush, |
2232 | #ifdef USE_PCI_DMA_API | ||
2233 | .agp_map_page = intel_agp_map_page, | ||
2234 | .agp_unmap_page = intel_agp_unmap_page, | ||
2235 | .agp_map_memory = intel_agp_map_memory, | ||
2236 | .agp_unmap_memory = intel_agp_unmap_memory, | ||
2237 | #endif | ||
2088 | }; | 2238 | }; |
2089 | 2239 | ||
2090 | static int find_gmch(u16 device) | 2240 | static int find_gmch(u16 device) |