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: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
b78e70c04c
commit
af1cbfb9bf
4 changed files with 87 additions and 24 deletions
|
@ -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 @@ free:
|
||||||
|
|
||||||
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 @@
|
||||||
#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)
|
||||||
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);
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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 @@
|
||||||
#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 {
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Add table
Reference in a new issue