Commit af1cbfb9 authored by Thierry Reding's avatar Thierry Reding
Browse files

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
Loading
Loading
Loading
Loading
+15 −3
Original line number Diff line number Diff line
@@ -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,9 +72,11 @@ free:

static void tegra_bo_unpin(struct device *dev, struct sg_table *sgt)
{
	if (sgt) {
		sg_free_table(sgt);
		kfree(sgt);
	}
}

static void *tegra_bo_mmap(struct host1x_bo *bo)
{
+4 −12
Original line number Diff line number Diff line
@@ -18,10 +18,6 @@
#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;
+64 −9
Original line number Diff line number Diff line
@@ -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);
	}

+4 −0
Original line number Diff line number Diff line
@@ -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;
};

/*