diff options
author | Thomas Hellstrom <thellstrom@vmware.com> | 2013-10-24 04:49:26 -0400 |
---|---|---|
committer | Thomas Hellstrom <thellstrom@vmware.com> | 2013-11-06 06:57:16 -0500 |
commit | d92d985177c495aab53c7167f310a7efb1853918 (patch) | |
tree | b8af3af9373dd0bbcb7df30b5e5a7b66dbf98801 /drivers | |
parent | 7aeb7448d8d02868ef30a6d08e856b2220319273 (diff) |
drm/vmwgfx: Use the linux DMA api to get valid device addresses of pages
The code handles three different cases:
1) physical page addresses. The ttm page array is used.
2) DMA subsystem addresses. A scatter-gather list is used.
3) Coherent pages. The ttm dma pool is used, together with the dma_ttm
array os dma_addr_t
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Jakob Bornecrantz <jakob@vmware.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 379 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 87 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 98 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c | 150 |
4 files changed, 620 insertions, 94 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index 96dc84dc34d0..7776e6f0aef6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | |||
@@ -141,37 +141,374 @@ struct ttm_placement vmw_srf_placement = { | |||
141 | }; | 141 | }; |
142 | 142 | ||
143 | struct vmw_ttm_tt { | 143 | struct vmw_ttm_tt { |
144 | struct ttm_tt ttm; | 144 | struct ttm_dma_tt dma_ttm; |
145 | struct vmw_private *dev_priv; | 145 | struct vmw_private *dev_priv; |
146 | int gmr_id; | 146 | int gmr_id; |
147 | struct sg_table sgt; | ||
148 | struct vmw_sg_table vsgt; | ||
149 | uint64_t sg_alloc_size; | ||
150 | bool mapped; | ||
147 | }; | 151 | }; |
148 | 152 | ||
153 | /** | ||
154 | * Helper functions to advance a struct vmw_piter iterator. | ||
155 | * | ||
156 | * @viter: Pointer to the iterator. | ||
157 | * | ||
158 | * These functions return false if past the end of the list, | ||
159 | * true otherwise. Functions are selected depending on the current | ||
160 | * DMA mapping mode. | ||
161 | */ | ||
162 | static bool __vmw_piter_non_sg_next(struct vmw_piter *viter) | ||
163 | { | ||
164 | return ++(viter->i) < viter->num_pages; | ||
165 | } | ||
166 | |||
167 | static bool __vmw_piter_sg_next(struct vmw_piter *viter) | ||
168 | { | ||
169 | return __sg_page_iter_next(&viter->iter); | ||
170 | } | ||
171 | |||
172 | |||
173 | /** | ||
174 | * Helper functions to return a pointer to the current page. | ||
175 | * | ||
176 | * @viter: Pointer to the iterator | ||
177 | * | ||
178 | * These functions return a pointer to the page currently | ||
179 | * pointed to by @viter. Functions are selected depending on the | ||
180 | * current mapping mode. | ||
181 | */ | ||
182 | static struct page *__vmw_piter_non_sg_page(struct vmw_piter *viter) | ||
183 | { | ||
184 | return viter->pages[viter->i]; | ||
185 | } | ||
186 | |||
187 | static struct page *__vmw_piter_sg_page(struct vmw_piter *viter) | ||
188 | { | ||
189 | return sg_page_iter_page(&viter->iter); | ||
190 | } | ||
191 | |||
192 | |||
193 | /** | ||
194 | * Helper functions to return the DMA address of the current page. | ||
195 | * | ||
196 | * @viter: Pointer to the iterator | ||
197 | * | ||
198 | * These functions return the DMA address of the page currently | ||
199 | * pointed to by @viter. Functions are selected depending on the | ||
200 | * current mapping mode. | ||
201 | */ | ||
202 | static dma_addr_t __vmw_piter_phys_addr(struct vmw_piter *viter) | ||
203 | { | ||
204 | return page_to_phys(viter->pages[viter->i]); | ||
205 | } | ||
206 | |||
207 | static dma_addr_t __vmw_piter_dma_addr(struct vmw_piter *viter) | ||
208 | { | ||
209 | return viter->addrs[viter->i]; | ||
210 | } | ||
211 | |||
212 | static dma_addr_t __vmw_piter_sg_addr(struct vmw_piter *viter) | ||
213 | { | ||
214 | return sg_page_iter_dma_address(&viter->iter); | ||
215 | } | ||
216 | |||
217 | |||
218 | /** | ||
219 | * vmw_piter_start - Initialize a struct vmw_piter. | ||
220 | * | ||
221 | * @viter: Pointer to the iterator to initialize | ||
222 | * @vsgt: Pointer to a struct vmw_sg_table to initialize from | ||
223 | * | ||
224 | * Note that we're following the convention of __sg_page_iter_start, so that | ||
225 | * the iterator doesn't point to a valid page after initialization; it has | ||
226 | * to be advanced one step first. | ||
227 | */ | ||
228 | void vmw_piter_start(struct vmw_piter *viter, const struct vmw_sg_table *vsgt, | ||
229 | unsigned long p_offset) | ||
230 | { | ||
231 | viter->i = p_offset - 1; | ||
232 | viter->num_pages = vsgt->num_pages; | ||
233 | switch (vsgt->mode) { | ||
234 | case vmw_dma_phys: | ||
235 | viter->next = &__vmw_piter_non_sg_next; | ||
236 | viter->dma_address = &__vmw_piter_phys_addr; | ||
237 | viter->page = &__vmw_piter_non_sg_page; | ||
238 | viter->pages = vsgt->pages; | ||
239 | break; | ||
240 | case vmw_dma_alloc_coherent: | ||
241 | viter->next = &__vmw_piter_non_sg_next; | ||
242 | viter->dma_address = &__vmw_piter_dma_addr; | ||
243 | viter->page = &__vmw_piter_non_sg_page; | ||
244 | viter->addrs = vsgt->addrs; | ||
245 | break; | ||
246 | case vmw_dma_map_populate: | ||
247 | case vmw_dma_map_bind: | ||
248 | viter->next = &__vmw_piter_sg_next; | ||
249 | viter->dma_address = &__vmw_piter_sg_addr; | ||
250 | viter->page = &__vmw_piter_sg_page; | ||
251 | __sg_page_iter_start(&viter->iter, vsgt->sgt->sgl, | ||
252 | vsgt->sgt->orig_nents, p_offset); | ||
253 | break; | ||
254 | default: | ||
255 | BUG(); | ||
256 | } | ||
257 | } | ||
258 | |||
259 | /** | ||
260 | * vmw_ttm_unmap_from_dma - unmap device addresses previsouly mapped for | ||
261 | * TTM pages | ||
262 | * | ||
263 | * @vmw_tt: Pointer to a struct vmw_ttm_backend | ||
264 | * | ||
265 | * Used to free dma mappings previously mapped by vmw_ttm_map_for_dma. | ||
266 | */ | ||
267 | static void vmw_ttm_unmap_from_dma(struct vmw_ttm_tt *vmw_tt) | ||
268 | { | ||
269 | struct device *dev = vmw_tt->dev_priv->dev->dev; | ||
270 | |||
271 | dma_unmap_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.nents, | ||
272 | DMA_BIDIRECTIONAL); | ||
273 | vmw_tt->sgt.nents = vmw_tt->sgt.orig_nents; | ||
274 | } | ||
275 | |||
276 | /** | ||
277 | * vmw_ttm_map_for_dma - map TTM pages to get device addresses | ||
278 | * | ||
279 | * @vmw_tt: Pointer to a struct vmw_ttm_backend | ||
280 | * | ||
281 | * This function is used to get device addresses from the kernel DMA layer. | ||
282 | * However, it's violating the DMA API in that when this operation has been | ||
283 | * performed, it's illegal for the CPU to write to the pages without first | ||
284 | * unmapping the DMA mappings, or calling dma_sync_sg_for_cpu(). It is | ||
285 | * therefore only legal to call this function if we know that the function | ||
286 | * dma_sync_sg_for_cpu() is a NOP, and dma_sync_sg_for_device() is at most | ||
287 | * a CPU write buffer flush. | ||
288 | */ | ||
289 | static int vmw_ttm_map_for_dma(struct vmw_ttm_tt *vmw_tt) | ||
290 | { | ||
291 | struct device *dev = vmw_tt->dev_priv->dev->dev; | ||
292 | int ret; | ||
293 | |||
294 | ret = dma_map_sg(dev, vmw_tt->sgt.sgl, vmw_tt->sgt.orig_nents, | ||
295 | DMA_BIDIRECTIONAL); | ||
296 | if (unlikely(ret == 0)) | ||
297 | return -ENOMEM; | ||
298 | |||
299 | vmw_tt->sgt.nents = ret; | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | /** | ||
305 | * vmw_ttm_map_dma - Make sure TTM pages are visible to the device | ||
306 | * | ||
307 | * @vmw_tt: Pointer to a struct vmw_ttm_tt | ||
308 | * | ||
309 | * Select the correct function for and make sure the TTM pages are | ||
310 | * visible to the device. Allocate storage for the device mappings. | ||
311 | * If a mapping has already been performed, indicated by the storage | ||
312 | * pointer being non NULL, the function returns success. | ||
313 | */ | ||
314 | static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt) | ||
315 | { | ||
316 | struct vmw_private *dev_priv = vmw_tt->dev_priv; | ||
317 | struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); | ||
318 | struct vmw_sg_table *vsgt = &vmw_tt->vsgt; | ||
319 | struct vmw_piter iter; | ||
320 | dma_addr_t old; | ||
321 | int ret = 0; | ||
322 | static size_t sgl_size; | ||
323 | static size_t sgt_size; | ||
324 | |||
325 | if (vmw_tt->mapped) | ||
326 | return 0; | ||
327 | |||
328 | vsgt->mode = dev_priv->map_mode; | ||
329 | vsgt->pages = vmw_tt->dma_ttm.ttm.pages; | ||
330 | vsgt->num_pages = vmw_tt->dma_ttm.ttm.num_pages; | ||
331 | vsgt->addrs = vmw_tt->dma_ttm.dma_address; | ||
332 | vsgt->sgt = &vmw_tt->sgt; | ||
333 | |||
334 | switch (dev_priv->map_mode) { | ||
335 | case vmw_dma_map_bind: | ||
336 | case vmw_dma_map_populate: | ||
337 | if (unlikely(!sgl_size)) { | ||
338 | sgl_size = ttm_round_pot(sizeof(struct scatterlist)); | ||
339 | sgt_size = ttm_round_pot(sizeof(struct sg_table)); | ||
340 | } | ||
341 | vmw_tt->sg_alloc_size = sgt_size + sgl_size * vsgt->num_pages; | ||
342 | ret = ttm_mem_global_alloc(glob, vmw_tt->sg_alloc_size, false, | ||
343 | true); | ||
344 | if (unlikely(ret != 0)) | ||
345 | return ret; | ||
346 | |||
347 | ret = sg_alloc_table_from_pages(&vmw_tt->sgt, vsgt->pages, | ||
348 | vsgt->num_pages, 0, | ||
349 | (unsigned long) | ||
350 | vsgt->num_pages << PAGE_SHIFT, | ||
351 | GFP_KERNEL); | ||
352 | if (unlikely(ret != 0)) | ||
353 | goto out_sg_alloc_fail; | ||
354 | |||
355 | if (vsgt->num_pages > vmw_tt->sgt.nents) { | ||
356 | uint64_t over_alloc = | ||
357 | sgl_size * (vsgt->num_pages - | ||
358 | vmw_tt->sgt.nents); | ||
359 | |||
360 | ttm_mem_global_free(glob, over_alloc); | ||
361 | vmw_tt->sg_alloc_size -= over_alloc; | ||
362 | } | ||
363 | |||
364 | ret = vmw_ttm_map_for_dma(vmw_tt); | ||
365 | if (unlikely(ret != 0)) | ||
366 | goto out_map_fail; | ||
367 | |||
368 | break; | ||
369 | default: | ||
370 | break; | ||
371 | } | ||
372 | |||
373 | old = ~((dma_addr_t) 0); | ||
374 | vmw_tt->vsgt.num_regions = 0; | ||
375 | for (vmw_piter_start(&iter, vsgt, 0); vmw_piter_next(&iter);) { | ||
376 | dma_addr_t cur = vmw_piter_dma_addr(&iter); | ||
377 | |||
378 | if (cur != old + PAGE_SIZE) | ||
379 | vmw_tt->vsgt.num_regions++; | ||
380 | old = cur; | ||
381 | } | ||
382 | |||
383 | vmw_tt->mapped = true; | ||
384 | return 0; | ||
385 | |||
386 | out_map_fail: | ||
387 | sg_free_table(vmw_tt->vsgt.sgt); | ||
388 | vmw_tt->vsgt.sgt = NULL; | ||
389 | out_sg_alloc_fail: | ||
390 | ttm_mem_global_free(glob, vmw_tt->sg_alloc_size); | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * vmw_ttm_unmap_dma - Tear down any TTM page device mappings | ||
396 | * | ||
397 | * @vmw_tt: Pointer to a struct vmw_ttm_tt | ||
398 | * | ||
399 | * Tear down any previously set up device DMA mappings and free | ||
400 | * any storage space allocated for them. If there are no mappings set up, | ||
401 | * this function is a NOP. | ||
402 | */ | ||
403 | static void vmw_ttm_unmap_dma(struct vmw_ttm_tt *vmw_tt) | ||
404 | { | ||
405 | struct vmw_private *dev_priv = vmw_tt->dev_priv; | ||
406 | |||
407 | if (!vmw_tt->vsgt.sgt) | ||
408 | return; | ||
409 | |||
410 | switch (dev_priv->map_mode) { | ||
411 | case vmw_dma_map_bind: | ||
412 | case vmw_dma_map_populate: | ||
413 | vmw_ttm_unmap_from_dma(vmw_tt); | ||
414 | sg_free_table(vmw_tt->vsgt.sgt); | ||
415 | vmw_tt->vsgt.sgt = NULL; | ||
416 | ttm_mem_global_free(vmw_mem_glob(dev_priv), | ||
417 | vmw_tt->sg_alloc_size); | ||
418 | break; | ||
419 | default: | ||
420 | break; | ||
421 | } | ||
422 | vmw_tt->mapped = false; | ||
423 | } | ||
424 | |||
149 | static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) | 425 | static int vmw_ttm_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) |
150 | { | 426 | { |
151 | struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); | 427 | struct vmw_ttm_tt *vmw_be = |
428 | container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); | ||
429 | int ret; | ||
430 | |||
431 | ret = vmw_ttm_map_dma(vmw_be); | ||
432 | if (unlikely(ret != 0)) | ||
433 | return ret; | ||
152 | 434 | ||
153 | vmw_be->gmr_id = bo_mem->start; | 435 | vmw_be->gmr_id = bo_mem->start; |
154 | 436 | ||
155 | return vmw_gmr_bind(vmw_be->dev_priv, ttm->pages, | 437 | return vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt, |
156 | ttm->num_pages, vmw_be->gmr_id); | 438 | ttm->num_pages, vmw_be->gmr_id); |
157 | } | 439 | } |
158 | 440 | ||
159 | static int vmw_ttm_unbind(struct ttm_tt *ttm) | 441 | static int vmw_ttm_unbind(struct ttm_tt *ttm) |
160 | { | 442 | { |
161 | struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); | 443 | struct vmw_ttm_tt *vmw_be = |
444 | container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); | ||
162 | 445 | ||
163 | vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id); | 446 | vmw_gmr_unbind(vmw_be->dev_priv, vmw_be->gmr_id); |
447 | |||
448 | if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind) | ||
449 | vmw_ttm_unmap_dma(vmw_be); | ||
450 | |||
164 | return 0; | 451 | return 0; |
165 | } | 452 | } |
166 | 453 | ||
167 | static void vmw_ttm_destroy(struct ttm_tt *ttm) | 454 | static void vmw_ttm_destroy(struct ttm_tt *ttm) |
168 | { | 455 | { |
169 | struct vmw_ttm_tt *vmw_be = container_of(ttm, struct vmw_ttm_tt, ttm); | 456 | struct vmw_ttm_tt *vmw_be = |
170 | 457 | container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); | |
171 | ttm_tt_fini(ttm); | 458 | |
459 | vmw_ttm_unmap_dma(vmw_be); | ||
460 | if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) | ||
461 | ttm_dma_tt_fini(&vmw_be->dma_ttm); | ||
462 | else | ||
463 | ttm_tt_fini(ttm); | ||
172 | kfree(vmw_be); | 464 | kfree(vmw_be); |
173 | } | 465 | } |
174 | 466 | ||
467 | static int vmw_ttm_populate(struct ttm_tt *ttm) | ||
468 | { | ||
469 | struct vmw_ttm_tt *vmw_tt = | ||
470 | container_of(ttm, struct vmw_ttm_tt, dma_ttm.ttm); | ||
471 | struct vmw_private *dev_priv = vmw_tt->dev_priv; | ||
472 | struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); | ||
473 | int ret; | ||
474 | |||
475 | if (ttm->state != tt_unpopulated) | ||
476 | return 0; | ||
477 | |||
478 | if (dev_priv->map_mode == vmw_dma_alloc_coherent) { | ||
479 | size_t size = | ||
480 | ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); | ||
481 | ret = ttm_mem_global_alloc(glob, size, false, true); | ||
482 | if (unlikely(ret != 0)) | ||
483 | return ret; | ||
484 | |||
485 | ret = ttm_dma_populate(&vmw_tt->dma_ttm, dev_priv->dev->dev); | ||
486 | if (unlikely(ret != 0)) | ||
487 | ttm_mem_global_free(glob, size); | ||
488 | } else | ||
489 | ret = ttm_pool_populate(ttm); | ||
490 | |||
491 | return ret; | ||
492 | } | ||
493 | |||
494 | static void vmw_ttm_unpopulate(struct ttm_tt *ttm) | ||
495 | { | ||
496 | struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt, | ||
497 | dma_ttm.ttm); | ||
498 | struct vmw_private *dev_priv = vmw_tt->dev_priv; | ||
499 | struct ttm_mem_global *glob = vmw_mem_glob(dev_priv); | ||
500 | |||
501 | vmw_ttm_unmap_dma(vmw_tt); | ||
502 | if (dev_priv->map_mode == vmw_dma_alloc_coherent) { | ||
503 | size_t size = | ||
504 | ttm_round_pot(ttm->num_pages * sizeof(dma_addr_t)); | ||
505 | |||
506 | ttm_dma_unpopulate(&vmw_tt->dma_ttm, dev_priv->dev->dev); | ||
507 | ttm_mem_global_free(glob, size); | ||
508 | } else | ||
509 | ttm_pool_unpopulate(ttm); | ||
510 | } | ||
511 | |||
175 | static struct ttm_backend_func vmw_ttm_func = { | 512 | static struct ttm_backend_func vmw_ttm_func = { |
176 | .bind = vmw_ttm_bind, | 513 | .bind = vmw_ttm_bind, |
177 | .unbind = vmw_ttm_unbind, | 514 | .unbind = vmw_ttm_unbind, |
@@ -183,20 +520,28 @@ struct ttm_tt *vmw_ttm_tt_create(struct ttm_bo_device *bdev, | |||
183 | struct page *dummy_read_page) | 520 | struct page *dummy_read_page) |
184 | { | 521 | { |
185 | struct vmw_ttm_tt *vmw_be; | 522 | struct vmw_ttm_tt *vmw_be; |
523 | int ret; | ||
186 | 524 | ||
187 | vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL); | 525 | vmw_be = kzalloc(sizeof(*vmw_be), GFP_KERNEL); |
188 | if (!vmw_be) | 526 | if (!vmw_be) |
189 | return NULL; | 527 | return NULL; |
190 | 528 | ||
191 | vmw_be->ttm.func = &vmw_ttm_func; | 529 | vmw_be->dma_ttm.ttm.func = &vmw_ttm_func; |
192 | vmw_be->dev_priv = container_of(bdev, struct vmw_private, bdev); | 530 | vmw_be->dev_priv = container_of(bdev, struct vmw_private, bdev); |
193 | 531 | ||
194 | if (ttm_tt_init(&vmw_be->ttm, bdev, size, page_flags, dummy_read_page)) { | 532 | if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) |
195 | kfree(vmw_be); | 533 | ret = ttm_dma_tt_init(&vmw_be->dma_ttm, bdev, size, page_flags, |
196 | return NULL; | 534 | dummy_read_page); |
197 | } | 535 | else |
198 | 536 | ret = ttm_tt_init(&vmw_be->dma_ttm.ttm, bdev, size, page_flags, | |
199 | return &vmw_be->ttm; | 537 | dummy_read_page); |
538 | if (unlikely(ret != 0)) | ||
539 | goto out_no_init; | ||
540 | |||
541 | return &vmw_be->dma_ttm.ttm; | ||
542 | out_no_init: | ||
543 | kfree(vmw_be); | ||
544 | return NULL; | ||
200 | } | 545 | } |
201 | 546 | ||
202 | int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) | 547 | int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) |
@@ -332,8 +677,8 @@ static int vmw_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible) | |||
332 | 677 | ||
333 | struct ttm_bo_driver vmw_bo_driver = { | 678 | struct ttm_bo_driver vmw_bo_driver = { |
334 | .ttm_tt_create = &vmw_ttm_tt_create, | 679 | .ttm_tt_create = &vmw_ttm_tt_create, |
335 | .ttm_tt_populate = &ttm_pool_populate, | 680 | .ttm_tt_populate = &vmw_ttm_populate, |
336 | .ttm_tt_unpopulate = &ttm_pool_unpopulate, | 681 | .ttm_tt_unpopulate = &vmw_ttm_unpopulate, |
337 | .invalidate_caches = vmw_invalidate_caches, | 682 | .invalidate_caches = vmw_invalidate_caches, |
338 | .init_mem_type = vmw_init_mem_type, | 683 | .init_mem_type = vmw_init_mem_type, |
339 | .evict_flags = vmw_evict_flags, | 684 | .evict_flags = vmw_evict_flags, |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 1a90f0a2f7e5..0b5c7818ebfb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <drm/ttm/ttm_bo_driver.h> | 32 | #include <drm/ttm/ttm_bo_driver.h> |
33 | #include <drm/ttm/ttm_object.h> | 33 | #include <drm/ttm/ttm_object.h> |
34 | #include <drm/ttm/ttm_module.h> | 34 | #include <drm/ttm/ttm_module.h> |
35 | #include <linux/dma_remapping.h> | ||
35 | 36 | ||
36 | #define VMWGFX_DRIVER_NAME "vmwgfx" | 37 | #define VMWGFX_DRIVER_NAME "vmwgfx" |
37 | #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" | 38 | #define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" |
@@ -185,6 +186,9 @@ static struct pci_device_id vmw_pci_id_list[] = { | |||
185 | MODULE_DEVICE_TABLE(pci, vmw_pci_id_list); | 186 | MODULE_DEVICE_TABLE(pci, vmw_pci_id_list); |
186 | 187 | ||
187 | static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON); | 188 | static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON); |
189 | static int vmw_force_iommu; | ||
190 | static int vmw_restrict_iommu; | ||
191 | static int vmw_force_coherent; | ||
188 | 192 | ||
189 | static int vmw_probe(struct pci_dev *, const struct pci_device_id *); | 193 | static int vmw_probe(struct pci_dev *, const struct pci_device_id *); |
190 | static void vmw_master_init(struct vmw_master *); | 194 | static void vmw_master_init(struct vmw_master *); |
@@ -193,6 +197,13 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, | |||
193 | 197 | ||
194 | MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); | 198 | MODULE_PARM_DESC(enable_fbdev, "Enable vmwgfx fbdev"); |
195 | module_param_named(enable_fbdev, enable_fbdev, int, 0600); | 199 | module_param_named(enable_fbdev, enable_fbdev, int, 0600); |
200 | MODULE_PARM_DESC(force_dma_api, "Force using the DMA API for TTM pages"); | ||
201 | module_param_named(force_dma_api, vmw_force_iommu, int, 0600); | ||
202 | MODULE_PARM_DESC(restrict_iommu, "Try to limit IOMMU usage for TTM pages"); | ||
203 | module_param_named(restrict_iommu, vmw_restrict_iommu, int, 0600); | ||
204 | MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages"); | ||
205 | module_param_named(force_coherent, vmw_force_coherent, int, 0600); | ||
206 | |||
196 | 207 | ||
197 | static void vmw_print_capabilities(uint32_t capabilities) | 208 | static void vmw_print_capabilities(uint32_t capabilities) |
198 | { | 209 | { |
@@ -427,12 +438,78 @@ static void vmw_get_initial_size(struct vmw_private *dev_priv) | |||
427 | dev_priv->initial_height = height; | 438 | dev_priv->initial_height = height; |
428 | } | 439 | } |
429 | 440 | ||
441 | /** | ||
442 | * vmw_dma_select_mode - Determine how DMA mappings should be set up for this | ||
443 | * system. | ||
444 | * | ||
445 | * @dev_priv: Pointer to a struct vmw_private | ||
446 | * | ||
447 | * This functions tries to determine the IOMMU setup and what actions | ||
448 | * need to be taken by the driver to make system pages visible to the | ||
449 | * device. | ||
450 | * If this function decides that DMA is not possible, it returns -EINVAL. | ||
451 | * The driver may then try to disable features of the device that require | ||
452 | * DMA. | ||
453 | */ | ||
454 | static int vmw_dma_select_mode(struct vmw_private *dev_priv) | ||
455 | { | ||
456 | const struct dma_map_ops *dma_ops = get_dma_ops(dev_priv->dev->dev); | ||
457 | static const char *names[vmw_dma_map_max] = { | ||
458 | [vmw_dma_phys] = "Using physical TTM page addresses.", | ||
459 | [vmw_dma_alloc_coherent] = "Using coherent TTM pages.", | ||
460 | [vmw_dma_map_populate] = "Keeping DMA mappings.", | ||
461 | [vmw_dma_map_bind] = "Giving up DMA mappings early."}; | ||
462 | |||
463 | #ifdef CONFIG_INTEL_IOMMU | ||
464 | if (intel_iommu_enabled) { | ||
465 | dev_priv->map_mode = vmw_dma_map_populate; | ||
466 | goto out_fixup; | ||
467 | } | ||
468 | #endif | ||
469 | |||
470 | if (!(vmw_force_iommu || vmw_force_coherent)) { | ||
471 | dev_priv->map_mode = vmw_dma_phys; | ||
472 | DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); | ||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | dev_priv->map_mode = vmw_dma_map_populate; | ||
477 | |||
478 | if (dma_ops->sync_single_for_cpu) | ||
479 | dev_priv->map_mode = vmw_dma_alloc_coherent; | ||
480 | #ifdef CONFIG_SWIOTLB | ||
481 | if (swiotlb_nr_tbl() == 0) | ||
482 | dev_priv->map_mode = vmw_dma_map_populate; | ||
483 | #endif | ||
484 | |||
485 | out_fixup: | ||
486 | if (dev_priv->map_mode == vmw_dma_map_populate && | ||
487 | vmw_restrict_iommu) | ||
488 | dev_priv->map_mode = vmw_dma_map_bind; | ||
489 | |||
490 | if (vmw_force_coherent) | ||
491 | dev_priv->map_mode = vmw_dma_alloc_coherent; | ||
492 | |||
493 | #if !defined(CONFIG_SWIOTLB) && !defined(CONFIG_INTEL_IOMMU) | ||
494 | /* | ||
495 | * No coherent page pool | ||
496 | */ | ||
497 | if (dev_priv->map_mode == vmw_dma_alloc_coherent) | ||
498 | return -EINVAL; | ||
499 | #endif | ||
500 | |||
501 | DRM_INFO("DMA map mode: %s\n", names[dev_priv->map_mode]); | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
430 | static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) | 506 | static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) |
431 | { | 507 | { |
432 | struct vmw_private *dev_priv; | 508 | struct vmw_private *dev_priv; |
433 | int ret; | 509 | int ret; |
434 | uint32_t svga_id; | 510 | uint32_t svga_id; |
435 | enum vmw_res_type i; | 511 | enum vmw_res_type i; |
512 | bool refuse_dma = false; | ||
436 | 513 | ||
437 | dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); | 514 | dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); |
438 | if (unlikely(dev_priv == NULL)) { | 515 | if (unlikely(dev_priv == NULL)) { |
@@ -481,6 +558,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) | |||
481 | } | 558 | } |
482 | 559 | ||
483 | dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); | 560 | dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); |
561 | ret = vmw_dma_select_mode(dev_priv); | ||
562 | if (unlikely(ret != 0)) { | ||
563 | DRM_INFO("Restricting capabilities due to IOMMU setup.\n"); | ||
564 | refuse_dma = true; | ||
565 | } | ||
484 | 566 | ||
485 | dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); | 567 | dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); |
486 | dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); | 568 | dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); |
@@ -558,8 +640,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) | |||
558 | } | 640 | } |
559 | 641 | ||
560 | dev_priv->has_gmr = true; | 642 | dev_priv->has_gmr = true; |
561 | if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, | 643 | if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || |
562 | dev_priv->max_gmr_ids) != 0) { | 644 | refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR, |
645 | dev_priv->max_gmr_ids) != 0) { | ||
563 | DRM_INFO("No GMR memory available. " | 646 | DRM_INFO("No GMR memory available. " |
564 | "Graphics memory resources are very limited.\n"); | 647 | "Graphics memory resources are very limited.\n"); |
565 | dev_priv->has_gmr = false; | 648 | dev_priv->has_gmr = false; |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 150ec64af617..e401d5dbcb96 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | |||
@@ -177,6 +177,58 @@ struct vmw_res_cache_entry { | |||
177 | struct vmw_resource_val_node *node; | 177 | struct vmw_resource_val_node *node; |
178 | }; | 178 | }; |
179 | 179 | ||
180 | /** | ||
181 | * enum vmw_dma_map_mode - indicate how to perform TTM page dma mappings. | ||
182 | */ | ||
183 | enum vmw_dma_map_mode { | ||
184 | vmw_dma_phys, /* Use physical page addresses */ | ||
185 | vmw_dma_alloc_coherent, /* Use TTM coherent pages */ | ||
186 | vmw_dma_map_populate, /* Unmap from DMA just after unpopulate */ | ||
187 | vmw_dma_map_bind, /* Unmap from DMA just before unbind */ | ||
188 | vmw_dma_map_max | ||
189 | }; | ||
190 | |||
191 | /** | ||
192 | * struct vmw_sg_table - Scatter/gather table for binding, with additional | ||
193 | * device-specific information. | ||
194 | * | ||
195 | * @sgt: Pointer to a struct sg_table with binding information | ||
196 | * @num_regions: Number of regions with device-address contigous pages | ||
197 | */ | ||
198 | struct vmw_sg_table { | ||
199 | enum vmw_dma_map_mode mode; | ||
200 | struct page **pages; | ||
201 | const dma_addr_t *addrs; | ||
202 | struct sg_table *sgt; | ||
203 | unsigned long num_regions; | ||
204 | unsigned long num_pages; | ||
205 | }; | ||
206 | |||
207 | /** | ||
208 | * struct vmw_piter - Page iterator that iterates over a list of pages | ||
209 | * and DMA addresses that could be either a scatter-gather list or | ||
210 | * arrays | ||
211 | * | ||
212 | * @pages: Array of page pointers to the pages. | ||
213 | * @addrs: DMA addresses to the pages if coherent pages are used. | ||
214 | * @iter: Scatter-gather page iterator. Current position in SG list. | ||
215 | * @i: Current position in arrays. | ||
216 | * @num_pages: Number of pages total. | ||
217 | * @next: Function to advance the iterator. Returns false if past the list | ||
218 | * of pages, true otherwise. | ||
219 | * @dma_address: Function to return the DMA address of the current page. | ||
220 | */ | ||
221 | struct vmw_piter { | ||
222 | struct page **pages; | ||
223 | const dma_addr_t *addrs; | ||
224 | struct sg_page_iter iter; | ||
225 | unsigned long i; | ||
226 | unsigned long num_pages; | ||
227 | bool (*next)(struct vmw_piter *); | ||
228 | dma_addr_t (*dma_address)(struct vmw_piter *); | ||
229 | struct page *(*page)(struct vmw_piter *); | ||
230 | }; | ||
231 | |||
180 | struct vmw_sw_context{ | 232 | struct vmw_sw_context{ |
181 | struct drm_open_hash res_ht; | 233 | struct drm_open_hash res_ht; |
182 | bool res_ht_initialized; | 234 | bool res_ht_initialized; |
@@ -358,6 +410,11 @@ struct vmw_private { | |||
358 | 410 | ||
359 | struct list_head res_lru[vmw_res_max]; | 411 | struct list_head res_lru[vmw_res_max]; |
360 | uint32_t used_memory_size; | 412 | uint32_t used_memory_size; |
413 | |||
414 | /* | ||
415 | * DMA mapping stuff. | ||
416 | */ | ||
417 | enum vmw_dma_map_mode map_mode; | ||
361 | }; | 418 | }; |
362 | 419 | ||
363 | static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) | 420 | static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) |
@@ -405,7 +462,7 @@ void vmw_3d_resource_dec(struct vmw_private *dev_priv, bool hide_svga); | |||
405 | */ | 462 | */ |
406 | 463 | ||
407 | extern int vmw_gmr_bind(struct vmw_private *dev_priv, | 464 | extern int vmw_gmr_bind(struct vmw_private *dev_priv, |
408 | struct page *pages[], | 465 | const struct vmw_sg_table *vsgt, |
409 | unsigned long num_pages, | 466 | unsigned long num_pages, |
410 | int gmr_id); | 467 | int gmr_id); |
411 | extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id); | 468 | extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id); |
@@ -568,6 +625,45 @@ extern struct ttm_placement vmw_evictable_placement; | |||
568 | extern struct ttm_placement vmw_srf_placement; | 625 | extern struct ttm_placement vmw_srf_placement; |
569 | extern struct ttm_bo_driver vmw_bo_driver; | 626 | extern struct ttm_bo_driver vmw_bo_driver; |
570 | extern int vmw_dma_quiescent(struct drm_device *dev); | 627 | extern int vmw_dma_quiescent(struct drm_device *dev); |
628 | extern void vmw_piter_start(struct vmw_piter *viter, | ||
629 | const struct vmw_sg_table *vsgt, | ||
630 | unsigned long p_offs); | ||
631 | |||
632 | /** | ||
633 | * vmw_piter_next - Advance the iterator one page. | ||
634 | * | ||
635 | * @viter: Pointer to the iterator to advance. | ||
636 | * | ||
637 | * Returns false if past the list of pages, true otherwise. | ||
638 | */ | ||
639 | static inline bool vmw_piter_next(struct vmw_piter *viter) | ||
640 | { | ||
641 | return viter->next(viter); | ||
642 | } | ||
643 | |||
644 | /** | ||
645 | * vmw_piter_dma_addr - Return the DMA address of the current page. | ||
646 | * | ||
647 | * @viter: Pointer to the iterator | ||
648 | * | ||
649 | * Returns the DMA address of the page pointed to by @viter. | ||
650 | */ | ||
651 | static inline dma_addr_t vmw_piter_dma_addr(struct vmw_piter *viter) | ||
652 | { | ||
653 | return viter->dma_address(viter); | ||
654 | } | ||
655 | |||
656 | /** | ||
657 | * vmw_piter_page - Return a pointer to the current page. | ||
658 | * | ||
659 | * @viter: Pointer to the iterator | ||
660 | * | ||
661 | * Returns the DMA address of the page pointed to by @viter. | ||
662 | */ | ||
663 | static inline struct page *vmw_piter_page(struct vmw_piter *viter) | ||
664 | { | ||
665 | return viter->page(viter); | ||
666 | } | ||
571 | 667 | ||
572 | /** | 668 | /** |
573 | * Command submission - vmwgfx_execbuf.c | 669 | * Command submission - vmwgfx_execbuf.c |
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index 1a0bf07fe54b..6d0952366f91 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c | |||
@@ -32,9 +32,11 @@ | |||
32 | #define VMW_PPN_SIZE (sizeof(unsigned long)) | 32 | #define VMW_PPN_SIZE (sizeof(unsigned long)) |
33 | /* A future safe maximum remap size. */ | 33 | /* A future safe maximum remap size. */ |
34 | #define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) | 34 | #define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) |
35 | #define DMA_ADDR_INVALID ((dma_addr_t) 0) | ||
36 | #define DMA_PAGE_INVALID 0UL | ||
35 | 37 | ||
36 | static int vmw_gmr2_bind(struct vmw_private *dev_priv, | 38 | static int vmw_gmr2_bind(struct vmw_private *dev_priv, |
37 | struct page *pages[], | 39 | struct vmw_piter *iter, |
38 | unsigned long num_pages, | 40 | unsigned long num_pages, |
39 | int gmr_id) | 41 | int gmr_id) |
40 | { | 42 | { |
@@ -81,11 +83,13 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv, | |||
81 | 83 | ||
82 | for (i = 0; i < nr; ++i) { | 84 | for (i = 0; i < nr; ++i) { |
83 | if (VMW_PPN_SIZE <= 4) | 85 | if (VMW_PPN_SIZE <= 4) |
84 | *cmd = page_to_pfn(*pages++); | 86 | *cmd = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; |
85 | else | 87 | else |
86 | *((uint64_t *)cmd) = page_to_pfn(*pages++); | 88 | *((uint64_t *)cmd) = vmw_piter_dma_addr(iter) >> |
89 | PAGE_SHIFT; | ||
87 | 90 | ||
88 | cmd += VMW_PPN_SIZE / sizeof(*cmd); | 91 | cmd += VMW_PPN_SIZE / sizeof(*cmd); |
92 | vmw_piter_next(iter); | ||
89 | } | 93 | } |
90 | 94 | ||
91 | num_pages -= nr; | 95 | num_pages -= nr; |
@@ -120,22 +124,54 @@ static void vmw_gmr2_unbind(struct vmw_private *dev_priv, | |||
120 | vmw_fifo_commit(dev_priv, define_size); | 124 | vmw_fifo_commit(dev_priv, define_size); |
121 | } | 125 | } |
122 | 126 | ||
127 | |||
128 | static void vmw_gmr_free_descriptors(struct device *dev, dma_addr_t desc_dma, | ||
129 | struct list_head *desc_pages) | ||
130 | { | ||
131 | struct page *page, *next; | ||
132 | struct svga_guest_mem_descriptor *page_virtual; | ||
133 | unsigned int desc_per_page = PAGE_SIZE / | ||
134 | sizeof(struct svga_guest_mem_descriptor) - 1; | ||
135 | |||
136 | if (list_empty(desc_pages)) | ||
137 | return; | ||
138 | |||
139 | list_for_each_entry_safe(page, next, desc_pages, lru) { | ||
140 | list_del_init(&page->lru); | ||
141 | |||
142 | if (likely(desc_dma != DMA_ADDR_INVALID)) { | ||
143 | dma_unmap_page(dev, desc_dma, PAGE_SIZE, | ||
144 | DMA_TO_DEVICE); | ||
145 | } | ||
146 | |||
147 | page_virtual = kmap_atomic(page); | ||
148 | desc_dma = page_virtual[desc_per_page].ppn << PAGE_SHIFT; | ||
149 | kunmap_atomic(page_virtual); | ||
150 | |||
151 | __free_page(page); | ||
152 | } | ||
153 | } | ||
154 | |||
123 | /** | 155 | /** |
124 | * FIXME: Adjust to the ttm lowmem / highmem storage to minimize | 156 | * FIXME: Adjust to the ttm lowmem / highmem storage to minimize |
125 | * the number of used descriptors. | 157 | * the number of used descriptors. |
158 | * | ||
126 | */ | 159 | */ |
127 | 160 | ||
128 | static int vmw_gmr_build_descriptors(struct list_head *desc_pages, | 161 | static int vmw_gmr_build_descriptors(struct device *dev, |
129 | struct page *pages[], | 162 | struct list_head *desc_pages, |
130 | unsigned long num_pages) | 163 | struct vmw_piter *iter, |
164 | unsigned long num_pages, | ||
165 | dma_addr_t *first_dma) | ||
131 | { | 166 | { |
132 | struct page *page, *next; | 167 | struct page *page; |
133 | struct svga_guest_mem_descriptor *page_virtual = NULL; | 168 | struct svga_guest_mem_descriptor *page_virtual = NULL; |
134 | struct svga_guest_mem_descriptor *desc_virtual = NULL; | 169 | struct svga_guest_mem_descriptor *desc_virtual = NULL; |
135 | unsigned int desc_per_page; | 170 | unsigned int desc_per_page; |
136 | unsigned long prev_pfn; | 171 | unsigned long prev_pfn; |
137 | unsigned long pfn; | 172 | unsigned long pfn; |
138 | int ret; | 173 | int ret; |
174 | dma_addr_t desc_dma; | ||
139 | 175 | ||
140 | desc_per_page = PAGE_SIZE / | 176 | desc_per_page = PAGE_SIZE / |
141 | sizeof(struct svga_guest_mem_descriptor) - 1; | 177 | sizeof(struct svga_guest_mem_descriptor) - 1; |
@@ -148,23 +184,12 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages, | |||
148 | } | 184 | } |
149 | 185 | ||
150 | list_add_tail(&page->lru, desc_pages); | 186 | list_add_tail(&page->lru, desc_pages); |
151 | |||
152 | /* | ||
153 | * Point previous page terminating descriptor to this | ||
154 | * page before unmapping it. | ||
155 | */ | ||
156 | |||
157 | if (likely(page_virtual != NULL)) { | ||
158 | desc_virtual->ppn = page_to_pfn(page); | ||
159 | kunmap_atomic(page_virtual); | ||
160 | } | ||
161 | |||
162 | page_virtual = kmap_atomic(page); | 187 | page_virtual = kmap_atomic(page); |
163 | desc_virtual = page_virtual - 1; | 188 | desc_virtual = page_virtual - 1; |
164 | prev_pfn = ~(0UL); | 189 | prev_pfn = ~(0UL); |
165 | 190 | ||
166 | while (likely(num_pages != 0)) { | 191 | while (likely(num_pages != 0)) { |
167 | pfn = page_to_pfn(*pages); | 192 | pfn = vmw_piter_dma_addr(iter) >> PAGE_SHIFT; |
168 | 193 | ||
169 | if (pfn != prev_pfn + 1) { | 194 | if (pfn != prev_pfn + 1) { |
170 | 195 | ||
@@ -181,104 +206,81 @@ static int vmw_gmr_build_descriptors(struct list_head *desc_pages, | |||
181 | } | 206 | } |
182 | prev_pfn = pfn; | 207 | prev_pfn = pfn; |
183 | --num_pages; | 208 | --num_pages; |
184 | ++pages; | 209 | vmw_piter_next(iter); |
185 | } | 210 | } |
186 | 211 | ||
187 | (++desc_virtual)->ppn = cpu_to_le32(0); | 212 | (++desc_virtual)->ppn = DMA_PAGE_INVALID; |
188 | desc_virtual->num_pages = cpu_to_le32(0); | 213 | desc_virtual->num_pages = cpu_to_le32(0); |
214 | kunmap_atomic(page_virtual); | ||
189 | } | 215 | } |
190 | 216 | ||
191 | if (likely(page_virtual != NULL)) | 217 | desc_dma = 0; |
218 | list_for_each_entry_reverse(page, desc_pages, lru) { | ||
219 | page_virtual = kmap_atomic(page); | ||
220 | page_virtual[desc_per_page].ppn = desc_dma >> PAGE_SHIFT; | ||
192 | kunmap_atomic(page_virtual); | 221 | kunmap_atomic(page_virtual); |
222 | desc_dma = dma_map_page(dev, page, 0, PAGE_SIZE, | ||
223 | DMA_TO_DEVICE); | ||
224 | |||
225 | if (unlikely(dma_mapping_error(dev, desc_dma))) | ||
226 | goto out_err; | ||
227 | } | ||
228 | *first_dma = desc_dma; | ||
193 | 229 | ||
194 | return 0; | 230 | return 0; |
195 | out_err: | 231 | out_err: |
196 | list_for_each_entry_safe(page, next, desc_pages, lru) { | 232 | vmw_gmr_free_descriptors(dev, DMA_ADDR_INVALID, desc_pages); |
197 | list_del_init(&page->lru); | ||
198 | __free_page(page); | ||
199 | } | ||
200 | return ret; | 233 | return ret; |
201 | } | 234 | } |
202 | 235 | ||
203 | static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages) | ||
204 | { | ||
205 | struct page *page, *next; | ||
206 | |||
207 | list_for_each_entry_safe(page, next, desc_pages, lru) { | ||
208 | list_del_init(&page->lru); | ||
209 | __free_page(page); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv, | 236 | static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv, |
214 | int gmr_id, struct list_head *desc_pages) | 237 | int gmr_id, dma_addr_t desc_dma) |
215 | { | 238 | { |
216 | struct page *page; | ||
217 | |||
218 | if (unlikely(list_empty(desc_pages))) | ||
219 | return; | ||
220 | |||
221 | page = list_entry(desc_pages->next, struct page, lru); | ||
222 | |||
223 | mutex_lock(&dev_priv->hw_mutex); | 239 | mutex_lock(&dev_priv->hw_mutex); |
224 | 240 | ||
225 | vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id); | 241 | vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id); |
226 | wmb(); | 242 | wmb(); |
227 | vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page)); | 243 | vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, desc_dma >> PAGE_SHIFT); |
228 | mb(); | 244 | mb(); |
229 | 245 | ||
230 | mutex_unlock(&dev_priv->hw_mutex); | 246 | mutex_unlock(&dev_priv->hw_mutex); |
231 | 247 | ||
232 | } | 248 | } |
233 | 249 | ||
234 | /** | ||
235 | * FIXME: Adjust to the ttm lowmem / highmem storage to minimize | ||
236 | * the number of used descriptors. | ||
237 | */ | ||
238 | |||
239 | static unsigned long vmw_gmr_count_descriptors(struct page *pages[], | ||
240 | unsigned long num_pages) | ||
241 | { | ||
242 | unsigned long prev_pfn = ~(0UL); | ||
243 | unsigned long pfn; | ||
244 | unsigned long descriptors = 0; | ||
245 | |||
246 | while (num_pages--) { | ||
247 | pfn = page_to_pfn(*pages++); | ||
248 | if (prev_pfn + 1 != pfn) | ||
249 | ++descriptors; | ||
250 | prev_pfn = pfn; | ||
251 | } | ||
252 | |||
253 | return descriptors; | ||
254 | } | ||
255 | |||
256 | int vmw_gmr_bind(struct vmw_private *dev_priv, | 250 | int vmw_gmr_bind(struct vmw_private *dev_priv, |
257 | struct page *pages[], | 251 | const struct vmw_sg_table *vsgt, |
258 | unsigned long num_pages, | 252 | unsigned long num_pages, |
259 | int gmr_id) | 253 | int gmr_id) |
260 | { | 254 | { |
261 | struct list_head desc_pages; | 255 | struct list_head desc_pages; |
256 | dma_addr_t desc_dma = 0; | ||
257 | struct device *dev = dev_priv->dev->dev; | ||
258 | struct vmw_piter data_iter; | ||
262 | int ret; | 259 | int ret; |
263 | 260 | ||
261 | vmw_piter_start(&data_iter, vsgt, 0); | ||
262 | |||
263 | if (unlikely(!vmw_piter_next(&data_iter))) | ||
264 | return 0; | ||
265 | |||
264 | if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) | 266 | if (likely(dev_priv->capabilities & SVGA_CAP_GMR2)) |
265 | return vmw_gmr2_bind(dev_priv, pages, num_pages, gmr_id); | 267 | return vmw_gmr2_bind(dev_priv, &data_iter, num_pages, gmr_id); |
266 | 268 | ||
267 | if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR))) | 269 | if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR))) |
268 | return -EINVAL; | 270 | return -EINVAL; |
269 | 271 | ||
270 | if (vmw_gmr_count_descriptors(pages, num_pages) > | 272 | if (vsgt->num_regions > dev_priv->max_gmr_descriptors) |
271 | dev_priv->max_gmr_descriptors) | ||
272 | return -EINVAL; | 273 | return -EINVAL; |
273 | 274 | ||
274 | INIT_LIST_HEAD(&desc_pages); | 275 | INIT_LIST_HEAD(&desc_pages); |
275 | 276 | ||
276 | ret = vmw_gmr_build_descriptors(&desc_pages, pages, num_pages); | 277 | ret = vmw_gmr_build_descriptors(dev, &desc_pages, &data_iter, |
278 | num_pages, &desc_dma); | ||
277 | if (unlikely(ret != 0)) | 279 | if (unlikely(ret != 0)) |
278 | return ret; | 280 | return ret; |
279 | 281 | ||
280 | vmw_gmr_fire_descriptors(dev_priv, gmr_id, &desc_pages); | 282 | vmw_gmr_fire_descriptors(dev_priv, gmr_id, desc_dma); |
281 | vmw_gmr_free_descriptors(&desc_pages); | 283 | vmw_gmr_free_descriptors(dev, desc_dma, &desc_pages); |
282 | 284 | ||
283 | return 0; | 285 | return 0; |
284 | } | 286 | } |