Commit af1cbfb9 authored by Thierry Reding's avatar Thierry Reding

gpu: host1x: Support DMA mapping of buffers

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: default avatarThierry Reding <treding@nvidia.com>
parent b78e70c0
...@@ -34,9 +34,19 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo, ...@@ -34,9 +34,19 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo,
struct sg_table *sgt; struct sg_table *sgt;
int err; 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; *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); sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt) if (!sgt)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -62,8 +72,10 @@ static struct sg_table *tegra_bo_pin(struct device *dev, struct host1x_bo *bo, ...@@ -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) static void tegra_bo_unpin(struct device *dev, struct sg_table *sgt)
{ {
sg_free_table(sgt); if (sgt) {
kfree(sgt); sg_free_table(sgt);
kfree(sgt);
}
} }
static void *tegra_bo_mmap(struct host1x_bo *bo) static void *tegra_bo_mmap(struct host1x_bo *bo)
......
...@@ -18,10 +18,6 @@ ...@@ -18,10 +18,6 @@
#include <trace/events/host1x.h> #include <trace/events/host1x.h>
#undef CREATE_TRACE_POINTS #undef CREATE_TRACE_POINTS
#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
#include <asm/dma-iommu.h>
#endif
#include "bus.h" #include "bus.h"
#include "channel.h" #include "channel.h"
#include "debug.h" #include "debug.h"
...@@ -276,17 +272,13 @@ static int host1x_probe(struct platform_device *pdev) ...@@ -276,17 +272,13 @@ static int host1x_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get reset: %d\n", err); dev_err(&pdev->dev, "failed to get reset: %d\n", err);
return 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)) if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL))
goto skip_iommu; goto skip_iommu;
if (iommu_get_domain_for_dev(&pdev->dev))
goto skip_iommu;
host->group = iommu_group_get(&pdev->dev); host->group = iommu_group_get(&pdev->dev);
if (host->group) { if (host->group) {
struct iommu_domain_geometry *geometry; struct iommu_domain_geometry *geometry;
......
...@@ -99,7 +99,8 @@ EXPORT_SYMBOL(host1x_job_add_gather); ...@@ -99,7 +99,8 @@ EXPORT_SYMBOL(host1x_job_add_gather);
static unsigned int pin_job(struct host1x *host, struct host1x_job *job) 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; unsigned int i;
int err; int err;
...@@ -107,8 +108,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -107,8 +108,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
for (i = 0; i < job->num_relocs; i++) { for (i = 0; i < job->num_relocs; i++) {
struct host1x_reloc *reloc = &job->relocs[i]; struct host1x_reloc *reloc = &job->relocs[i];
dma_addr_t phys_addr, *phys;
struct sg_table *sgt; struct sg_table *sgt;
dma_addr_t phys_addr;
reloc->target.bo = host1x_bo_get(reloc->target.bo); reloc->target.bo = host1x_bo_get(reloc->target.bo);
if (!reloc->target.bo) { if (!reloc->target.bo) {
...@@ -116,12 +117,51 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -116,12 +117,51 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin; 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)) { if (IS_ERR(sgt)) {
err = PTR_ERR(sgt); err = PTR_ERR(sgt);
goto unpin; 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->addr_phys[job->num_unpins] = phys_addr;
job->unpins[job->num_unpins].bo = reloc->target.bo; job->unpins[job->num_unpins].bo = reloc->target.bo;
job->unpins[job->num_unpins].sgt = sgt; job->unpins[job->num_unpins].sgt = sgt;
...@@ -144,7 +184,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -144,7 +184,7 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin; 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)) { if (IS_ERR(sgt)) {
err = PTR_ERR(sgt); err = PTR_ERR(sgt);
goto unpin; goto unpin;
...@@ -172,15 +212,24 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -172,15 +212,24 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto unpin; goto unpin;
} }
job->addr_phys[job->num_unpins] =
iova_dma_addr(&host->iova, alloc);
job->unpins[job->num_unpins].size = gather_size; job->unpins[job->num_unpins].size = gather_size;
phys_addr = iova_dma_addr(&host->iova, alloc);
} else { } 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].bo = g->bo;
job->unpins[job->num_unpins].sgt = sgt; job->unpins[job->num_unpins].sgt = sgt;
job->num_unpins++; job->num_unpins++;
...@@ -567,6 +616,8 @@ void host1x_job_unpin(struct host1x_job *job) ...@@ -567,6 +616,8 @@ void host1x_job_unpin(struct host1x_job *job)
for (i = 0; i < job->num_unpins; i++) { for (i = 0; i < job->num_unpins; i++) {
struct host1x_job_unpin_data *unpin = &job->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) && if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) &&
unpin->size && host->domain) { unpin->size && host->domain) {
...@@ -576,7 +627,11 @@ void host1x_job_unpin(struct host1x_job *job) ...@@ -576,7 +627,11 @@ void host1x_job_unpin(struct host1x_job *job)
iova_pfn(&host->iova, job->addr_phys[i])); 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); host1x_bo_put(unpin->bo);
} }
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#ifndef __HOST1X_JOB_H #ifndef __HOST1X_JOB_H
#define __HOST1X_JOB_H #define __HOST1X_JOB_H
#include <linux/dma-direction.h>
struct host1x_job_gather { struct host1x_job_gather {
unsigned int words; unsigned int words;
dma_addr_t base; dma_addr_t base;
...@@ -19,7 +21,9 @@ struct host1x_job_gather { ...@@ -19,7 +21,9 @@ struct host1x_job_gather {
struct host1x_job_unpin_data { struct host1x_job_unpin_data {
struct host1x_bo *bo; struct host1x_bo *bo;
struct sg_table *sgt; struct sg_table *sgt;
struct device *dev;
size_t size; size_t size;
enum dma_data_direction dir;
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment