Merge tag 'ras_for_3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp into...
[cascardo/linux.git] / drivers / gpu / drm / tegra / gem.c
index da32086..8777b7f 100644 (file)
@@ -216,32 +216,58 @@ static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo)
        }
 }
 
-static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo,
-                             size_t size)
+static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo)
 {
+       struct scatterlist *s;
+       struct sg_table *sgt;
+       unsigned int i;
+
        bo->pages = drm_gem_get_pages(&bo->gem);
        if (IS_ERR(bo->pages))
                return PTR_ERR(bo->pages);
 
-       bo->num_pages = size >> PAGE_SHIFT;
-
-       bo->sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages);
-       if (IS_ERR(bo->sgt)) {
-               drm_gem_put_pages(&bo->gem, bo->pages, false, false);
-               return PTR_ERR(bo->sgt);
+       bo->num_pages = bo->gem.size >> PAGE_SHIFT;
+
+       sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages);
+       if (IS_ERR(sgt))
+               goto put_pages;
+
+       /*
+        * Fake up the SG table so that dma_map_sg() can be used to flush the
+        * pages associated with it. Note that this relies on the fact that
+        * the DMA API doesn't hook into IOMMU on Tegra, therefore mapping is
+        * only cache maintenance.
+        *
+        * TODO: Replace this by drm_clflash_sg() once it can be implemented
+        * without relying on symbols that are not exported.
+        */
+       for_each_sg(sgt->sgl, s, sgt->nents, i)
+               sg_dma_address(s) = sg_phys(s);
+
+       if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0) {
+               sgt = ERR_PTR(-ENOMEM);
+               goto release_sgt;
        }
 
+       bo->sgt = sgt;
+
        return 0;
+
+release_sgt:
+       sg_free_table(sgt);
+       kfree(sgt);
+put_pages:
+       drm_gem_put_pages(&bo->gem, bo->pages, false, false);
+       return PTR_ERR(sgt);
 }
 
-static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo,
-                         size_t size)
+static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo)
 {
        struct tegra_drm *tegra = drm->dev_private;
        int err;
 
        if (tegra->domain) {
-               err = tegra_bo_get_pages(drm, bo, size);
+               err = tegra_bo_get_pages(drm, bo);
                if (err < 0)
                        return err;
 
@@ -251,6 +277,8 @@ static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo,
                        return err;
                }
        } else {
+               size_t size = bo->gem.size;
+
                bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr,
                                                   GFP_KERNEL | __GFP_NOWARN);
                if (!bo->vaddr) {
@@ -274,7 +302,7 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, size_t size,
        if (IS_ERR(bo))
                return bo;
 
-       err = tegra_bo_alloc(drm, bo, size);
+       err = tegra_bo_alloc(drm, bo);
        if (err < 0)
                goto release;