]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
gpu: host1x: Support DMA mapping of buffers
authorThierry Reding <treding@nvidia.com>
Mon, 28 Oct 2019 12:37:13 +0000 (13:37 +0100)
committerThierry Reding <treding@nvidia.com>
Tue, 29 Oct 2019 14:04:35 +0000 (15:04 +0100)
If host1x_bo_pin() returns an SG table, create a DMA mapping for the
buffer. For buffers that the host1x client has already mapped itself,
host1x_bo_pin() returns NULL and the existing DMA address is used.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/gem.c
drivers/gpu/host1x/dev.c
drivers/gpu/host1x/job.c
drivers/gpu/host1x/job.h

index 564ef60f67c2f8a5cba0ed0e9083baa7ad75f3b3..746dae32c48490fcb8220ba74d6f042d94d69dae 100644 (file)
@@ -34,9 +34,19 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo,
        struct sg_table *sgt;
        int err;
 
-       if (phys)
+       /*
+        * If we've manually mapped the buffer object through the IOMMU, make
+        * sure to return the IOVA address of our mapping.
+        */
+       if (phys && obj->mm) {
                *phys = obj->iova;
+               return NULL;
+       }
 
+       /*
+        * If we don't have a mapping for this buffer yet, return an SG table
+        * so that host1x can do the mapping for us via the DMA API.
+        */
        sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
        if (!sgt)
                return ERR_PTR(-ENOMEM);
@@ -62,8 +72,10 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo,
 
 static void tegra_bo_unpin(struct device *dev, struct sg_table *sgt)
 {
-       sg_free_table(sgt);
-       kfree(sgt);
+       if (sgt) {
+               sg_free_table(sgt);
+               kfree(sgt);
+       }
 }
 
 static void *tegra_bo_mmap(struct host1x_bo *bo)
index f30b8447a319b78e8b2a3bb777658103dd526e76..5bdc484398f41f6b2d4ba0dd2b4b983faf668bba 100644 (file)
 #include <trace/events/host1x.h>
 #undef CREATE_TRACE_POINTS
 
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
-#include <asm/dma-iommu.h>
-#endif
-
 #include "bus.h"
 #include "channel.h"
 #include "debug.h"
@@ -276,17 +272,13 @@ static int host1x_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "failed to get reset: %d\n", err);
                return err;
        }
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
-       if (host->dev->archdata.mapping) {
-               struct dma_iommu_mapping *mapping =
-                               to_dma_iommu_mapping(host->dev);
-               arm_iommu_detach_device(host->dev);
-               arm_iommu_release_mapping(mapping);
-       }
-#endif
+
        if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL))
                goto skip_iommu;
 
+       if (iommu_get_domain_for_dev(&pdev->dev))
+               goto skip_iommu;
+
        host->group = iommu_group_get(&pdev->dev);
        if (host->group) {
                struct iommu_domain_geometry *geometry;
index 2e0c3e0ca1fa2e93ea66a40e5493d680ac563d00..25ca54de8fc583fd8891eb7eeb706416222e3f19 100644 (file)
@@ -99,7 +99,8 @@ EXPORT_SYMBOL(host1x_job_add_gather);
 
 static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
 {
-       struct device *dev = job->client->dev;
+       struct host1x_client *client = job->client;
+       struct device *dev = client->dev;
        unsigned int i;
        int err;
 
@@ -107,8 +108,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
 
        for (i = 0; i < job->num_relocs; i++) {
                struct host1x_reloc *reloc = &job->relocs[i];
+               dma_addr_t phys_addr, *phys;
                struct sg_table *sgt;
-               dma_addr_t phys_addr;
 
                reloc->target.bo = host1x_bo_get(reloc->target.bo);
                if (!reloc->target.bo) {
@@ -116,12 +117,51 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
                        goto unpin;
                }
 
-               sgt = host1x_bo_pin(dev, reloc->target.bo, &phys_addr);
+               if (client->group)
+                       phys = &phys_addr;
+               else
+                       phys = NULL;
+
+               sgt = host1x_bo_pin(dev, reloc->target.bo, phys);
                if (IS_ERR(sgt)) {
                        err = PTR_ERR(sgt);
                        goto unpin;
                }
 
+               if (sgt) {
+                       unsigned long mask = HOST1X_RELOC_READ |
+                                            HOST1X_RELOC_WRITE;
+                       enum dma_data_direction dir;
+
+                       switch (reloc->flags & mask) {
+                       case HOST1X_RELOC_READ:
+                               dir = DMA_TO_DEVICE;
+                               break;
+
+                       case HOST1X_RELOC_WRITE:
+                               dir = DMA_FROM_DEVICE;
+                               break;
+
+                       case HOST1X_RELOC_READ | HOST1X_RELOC_WRITE:
+                               dir = DMA_BIDIRECTIONAL;
+                               break;
+
+                       default:
+                               err = -EINVAL;
+                               goto unpin;
+                       }
+
+                       err = dma_map_sg(dev, sgt->sgl, sgt->nents, dir);
+                       if (!err) {
+                               err = -ENOMEM;
+                               goto unpin;
+                       }
+
+                       job->unpins[job->num_unpins].dev = dev;
+                       job->unpins[job->num_unpins].dir = dir;
+                       phys_addr = sg_dma_address(sgt->sgl);
+               }
+
                job->addr_phys[job->num_unpins] = phys_addr;
                job->unpins[job->num_unpins].bo = reloc->target.bo;
                job->unpins[job->num_unpins].sgt = sgt;
@@ -144,7 +184,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
                        goto unpin;
                }
 
-               sgt = host1x_bo_pin(host->dev, g->bo, &phys_addr);
+               sgt = host1x_bo_pin(host->dev, g->bo, NULL);
                if (IS_ERR(sgt)) {
                        err = PTR_ERR(sgt);
                        goto unpin;
@@ -172,15 +212,24 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
                                goto unpin;
                        }
 
-                       job->addr_phys[job->num_unpins] =
-                               iova_dma_addr(&host->iova, alloc);
                        job->unpins[job->num_unpins].size = gather_size;
+                       phys_addr = iova_dma_addr(&host->iova, alloc);
                } else {
-                       job->addr_phys[job->num_unpins] = phys_addr;
+                       err = dma_map_sg(host->dev, sgt->sgl, sgt->nents,
+                                        DMA_TO_DEVICE);
+                       if (!err) {
+                               err = -ENOMEM;
+                               goto unpin;
+                       }
+
+                       job->unpins[job->num_unpins].dev = host->dev;
+                       phys_addr = sg_dma_address(sgt->sgl);
                }
 
-               job->gather_addr_phys[i] = job->addr_phys[job->num_unpins];
+               job->addr_phys[job->num_unpins] = phys_addr;
+               job->gather_addr_phys[i] = phys_addr;
 
+               job->unpins[job->num_unpins].dir = DMA_TO_DEVICE;
                job->unpins[job->num_unpins].bo = g->bo;
                job->unpins[job->num_unpins].sgt = sgt;
                job->num_unpins++;
@@ -567,6 +616,8 @@ void host1x_job_unpin(struct host1x_job *job)
 
        for (i = 0; i < job->num_unpins; i++) {
                struct host1x_job_unpin_data *unpin = &job->unpins[i];
+               struct device *dev = unpin->dev ?: host->dev;
+               struct sg_table *sgt = unpin->sgt;
 
                if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) &&
                    unpin->size && host->domain) {
@@ -576,7 +627,11 @@ void host1x_job_unpin(struct host1x_job *job)
                                iova_pfn(&host->iova, job->addr_phys[i]));
                }
 
-               host1x_bo_unpin(host->dev, unpin->bo, unpin->sgt);
+               if (unpin->dev && sgt)
+                       dma_unmap_sg(unpin->dev, sgt->sgl, sgt->nents,
+                                    unpin->dir);
+
+               host1x_bo_unpin(dev, unpin->bo, sgt);
                host1x_bo_put(unpin->bo);
        }
 
index 62b8805e6b357f91b73b4ccec8829dfdedfaef57..94bc2e4ae241d202fd61e74b90425ca26bb17a35 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef __HOST1X_JOB_H
 #define __HOST1X_JOB_H
 
+#include <linux/dma-direction.h>
+
 struct host1x_job_gather {
        unsigned int words;
        dma_addr_t base;
@@ -19,7 +21,9 @@ struct host1x_job_gather {
 struct host1x_job_unpin_data {
        struct host1x_bo *bo;
        struct sg_table *sgt;
+       struct device *dev;
        size_t size;
+       enum dma_data_direction dir;
 };
 
 /*