diff options
Diffstat (limited to 'drivers/gpu/host1x/job.c')
| -rw-r--r-- | drivers/gpu/host1x/job.c | 160 | 
1 files changed, 57 insertions, 103 deletions
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index 0eef6df7c89e..5e8c183167b7 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c @@ -134,20 +134,20 @@ EXPORT_SYMBOL(host1x_job_add_wait);  static unsigned int pin_job(struct host1x *host, struct host1x_job *job)  { +	unsigned long mask = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE;  	struct host1x_client *client = job->client;  	struct device *dev = client->dev;  	struct host1x_job_gather *g; -	struct iommu_domain *domain; -	struct sg_table *sgt;  	unsigned int i;  	int err; -	domain = iommu_get_domain_for_dev(dev);  	job->num_unpins = 0;  	for (i = 0; i < job->num_relocs; i++) {  		struct host1x_reloc *reloc = &job->relocs[i]; -		dma_addr_t phys_addr, *phys; +		enum dma_data_direction direction; +		struct host1x_bo_mapping *map; +		struct host1x_bo *bo;  		reloc->target.bo = host1x_bo_get(reloc->target.bo);  		if (!reloc->target.bo) { @@ -155,64 +155,44 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)  			goto unpin;  		} -		/* -		 * If the client device is not attached to an IOMMU, the -		 * physical address of the buffer object can be used. -		 * -		 * Similarly, when an IOMMU domain is shared between all -		 * host1x clients, the IOVA is already available, so no -		 * need to map the buffer object again. -		 * -		 * XXX Note that this isn't always safe to do because it -		 * relies on an assumption that no cache maintenance is -		 * needed on the buffer objects. -		 */ -		if (!domain || 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; -		} +		bo = reloc->target.bo; -		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; +		switch (reloc->flags & mask) { +		case HOST1X_RELOC_READ: +			direction = DMA_TO_DEVICE; +			break; -			case HOST1X_RELOC_WRITE: -				dir = DMA_FROM_DEVICE; -				break; +		case HOST1X_RELOC_WRITE: +			direction = DMA_FROM_DEVICE; +			break; -			case HOST1X_RELOC_READ | HOST1X_RELOC_WRITE: -				dir = DMA_BIDIRECTIONAL; -				break; +		case HOST1X_RELOC_READ | HOST1X_RELOC_WRITE: +			direction = DMA_BIDIRECTIONAL; +			break; -			default: -				err = -EINVAL; -				goto unpin; -			} +		default: +			err = -EINVAL; +			goto unpin; +		} -			err = dma_map_sgtable(dev, sgt, dir, 0); -			if (err) -				goto unpin; +		map = host1x_bo_pin(dev, bo, direction, &client->cache); +		if (IS_ERR(map)) { +			err = PTR_ERR(map); +			goto unpin; +		} -			job->unpins[job->num_unpins].dev = dev; -			job->unpins[job->num_unpins].dir = dir; -			phys_addr = sg_dma_address(sgt->sgl); +		/* +		 * host1x clients are generally not able to do scatter-gather themselves, so fail +		 * if the buffer is discontiguous and we fail to map its SG table to a single +		 * contiguous chunk of I/O virtual memory. +		 */ +		if (map->chunks > 1) { +			err = -EINVAL; +			goto unpin;  		} -		job->addr_phys[job->num_unpins] = phys_addr; -		job->unpins[job->num_unpins].bo = reloc->target.bo; -		job->unpins[job->num_unpins].sgt = sgt; +		job->addr_phys[job->num_unpins] = map->phys; +		job->unpins[job->num_unpins].map = map;  		job->num_unpins++;  	} @@ -224,12 +204,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)  		return 0;  	for (i = 0; i < job->num_cmds; i++) { +		struct host1x_bo_mapping *map;  		size_t gather_size = 0;  		struct scatterlist *sg; -		dma_addr_t phys_addr;  		unsigned long shift;  		struct iova *alloc; -		dma_addr_t *phys;  		unsigned int j;  		if (job->cmds[i].is_wait) @@ -243,25 +222,16 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)  			goto unpin;  		} -		/** -		 * If the host1x is not attached to an IOMMU, there is no need -		 * to map the buffer object for the host1x, since the physical -		 * address can simply be used. -		 */ -		if (!iommu_get_domain_for_dev(host->dev)) -			phys = &phys_addr; -		else -			phys = NULL; - -		sgt = host1x_bo_pin(host->dev, g->bo, phys); -		if (IS_ERR(sgt)) { -			err = PTR_ERR(sgt); -			goto put; +		map = host1x_bo_pin(host->dev, g->bo, DMA_TO_DEVICE, &host->cache); +		if (IS_ERR(map)) { +			err = PTR_ERR(map); +			goto unpin;  		}  		if (host->domain) { -			for_each_sgtable_sg(sgt, sg, j) +			for_each_sgtable_sg(map->sgt, sg, j)  				gather_size += sg->length; +  			gather_size = iova_align(&host->iova, gather_size);  			shift = iova_shift(&host->iova); @@ -272,33 +242,23 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)  				goto put;  			} -			err = iommu_map_sgtable(host->domain, -					iova_dma_addr(&host->iova, alloc), -					sgt, IOMMU_READ); +			err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc), +						map->sgt, IOMMU_READ);  			if (err == 0) {  				__free_iova(&host->iova, alloc);  				err = -EINVAL;  				goto put;  			} -			job->unpins[job->num_unpins].size = gather_size; -			phys_addr = iova_dma_addr(&host->iova, alloc); -		} else if (sgt) { -			err = dma_map_sgtable(host->dev, sgt, DMA_TO_DEVICE, 0); -			if (err) -				goto put; - -			job->unpins[job->num_unpins].dir = DMA_TO_DEVICE; -			job->unpins[job->num_unpins].dev = host->dev; -			phys_addr = sg_dma_address(sgt->sgl); +			map->phys = iova_dma_addr(&host->iova, alloc); +			map->size = gather_size;  		} -		job->addr_phys[job->num_unpins] = phys_addr; -		job->gather_addr_phys[i] = phys_addr; - -		job->unpins[job->num_unpins].bo = g->bo; -		job->unpins[job->num_unpins].sgt = sgt; +		job->addr_phys[job->num_unpins] = map->phys; +		job->unpins[job->num_unpins].map = map;  		job->num_unpins++; + +		job->gather_addr_phys[i] = map->phys;  	}  	return 0; @@ -690,22 +650,16 @@ void host1x_job_unpin(struct host1x_job *job)  	unsigned int i;  	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 (!job->enable_firewall && unpin->size && host->domain) { -			iommu_unmap(host->domain, job->addr_phys[i], -				    unpin->size); -			free_iova(&host->iova, -				iova_pfn(&host->iova, job->addr_phys[i])); -		} +		struct host1x_bo_mapping *map = job->unpins[i].map; +		struct host1x_bo *bo = map->bo; -		if (unpin->dev && sgt) -			dma_unmap_sgtable(unpin->dev, sgt, unpin->dir, 0); +		if (!job->enable_firewall && map->size && host->domain) { +			iommu_unmap(host->domain, job->addr_phys[i], map->size); +			free_iova(&host->iova, iova_pfn(&host->iova, job->addr_phys[i])); +		} -		host1x_bo_unpin(dev, unpin->bo, sgt); -		host1x_bo_put(unpin->bo); +		host1x_bo_unpin(map); +		host1x_bo_put(bo);  	}  	job->num_unpins = 0;  |