Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+87 -24
+15 -3
drivers/gpu/drm/tegra/gem.c
··· 34 34 struct sg_table *sgt; 35 35 int err; 36 36 37 - if (phys) 37 + /* 38 + * If we've manually mapped the buffer object through the IOMMU, make 39 + * sure to return the IOVA address of our mapping. 40 + */ 41 + if (phys && obj->mm) { 38 42 *phys = obj->iova; 43 + return NULL; 44 + } 39 45 46 + /* 47 + * If we don't have a mapping for this buffer yet, return an SG table 48 + * so that host1x can do the mapping for us via the DMA API. 49 + */ 40 50 sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); 41 51 if (!sgt) 42 52 return ERR_PTR(-ENOMEM); ··· 72 62 73 63 static void tegra_bo_unpin(struct device *dev, struct sg_table *sgt) 74 64 { 75 - sg_free_table(sgt); 76 - kfree(sgt); 65 + if (sgt) { 66 + sg_free_table(sgt); 67 + kfree(sgt); 68 + } 77 69 } 78 70 79 71 static void *tegra_bo_mmap(struct host1x_bo *bo)
+4 -12
drivers/gpu/host1x/dev.c
··· 18 18 #include <trace/events/host1x.h> 19 19 #undef CREATE_TRACE_POINTS 20 20 21 - #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) 22 - #include <asm/dma-iommu.h> 23 - #endif 24 - 25 21 #include "bus.h" 26 22 #include "channel.h" 27 23 #include "debug.h" ··· 272 276 dev_err(&pdev->dev, "failed to get reset: %d\n", err); 273 277 return err; 274 278 } 275 - #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) 276 - if (host->dev->archdata.mapping) { 277 - struct dma_iommu_mapping *mapping = 278 - to_dma_iommu_mapping(host->dev); 279 - arm_iommu_detach_device(host->dev); 280 - arm_iommu_release_mapping(mapping); 281 - } 282 - #endif 279 + 283 280 if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) 281 + goto skip_iommu; 282 + 283 + if (iommu_get_domain_for_dev(&pdev->dev)) 284 284 goto skip_iommu; 285 285 286 286 host->group = iommu_group_get(&pdev->dev);
+64 -9
drivers/gpu/host1x/job.c
··· 99 99 100 100 static unsigned int pin_job(struct host1x *host, struct host1x_job *job) 101 101 { 102 - struct device *dev = job->client->dev; 102 + struct host1x_client *client = job->client; 103 + struct device *dev = client->dev; 103 104 unsigned int i; 104 105 int err; 105 106 ··· 108 107 109 108 for (i = 0; i < job->num_relocs; i++) { 110 109 struct host1x_reloc *reloc = &job->relocs[i]; 110 + dma_addr_t phys_addr, *phys; 111 111 struct sg_table *sgt; 112 - dma_addr_t phys_addr; 113 112 114 113 reloc->target.bo = host1x_bo_get(reloc->target.bo); 115 114 if (!reloc->target.bo) { ··· 117 116 goto unpin; 118 117 } 119 118 120 - sgt = host1x_bo_pin(dev, reloc->target.bo, &phys_addr); 119 + if (client->group) 120 + phys = &phys_addr; 121 + else 122 + phys = NULL; 123 + 124 + sgt = host1x_bo_pin(dev, reloc->target.bo, phys); 121 125 if (IS_ERR(sgt)) { 122 126 err = PTR_ERR(sgt); 123 127 goto unpin; 128 + } 129 + 130 + if (sgt) { 131 + unsigned long mask = HOST1X_RELOC_READ | 132 + HOST1X_RELOC_WRITE; 133 + enum dma_data_direction dir; 134 + 135 + switch (reloc->flags & mask) { 136 + case HOST1X_RELOC_READ: 137 + dir = DMA_TO_DEVICE; 138 + break; 139 + 140 + case HOST1X_RELOC_WRITE: 141 + dir = DMA_FROM_DEVICE; 142 + break; 143 + 144 + case HOST1X_RELOC_READ | HOST1X_RELOC_WRITE: 145 + dir = DMA_BIDIRECTIONAL; 146 + break; 147 + 148 + default: 149 + err = -EINVAL; 150 + goto unpin; 151 + } 152 + 153 + err = dma_map_sg(dev, sgt->sgl, sgt->nents, dir); 154 + if (!err) { 155 + err = -ENOMEM; 156 + goto unpin; 157 + } 158 + 159 + job->unpins[job->num_unpins].dev = dev; 160 + job->unpins[job->num_unpins].dir = dir; 161 + phys_addr = sg_dma_address(sgt->sgl); 124 162 } 125 163 126 164 job->addr_phys[job->num_unpins] = phys_addr; ··· 184 144 goto unpin; 185 145 } 186 146 187 - sgt = host1x_bo_pin(host->dev, g->bo, &phys_addr); 147 + sgt = host1x_bo_pin(host->dev, g->bo, NULL); 188 148 if (IS_ERR(sgt)) { 189 149 err = PTR_ERR(sgt); 190 150 goto unpin; ··· 212 172 goto unpin; 213 173 } 214 174 215 - job->addr_phys[job->num_unpins] = 216 - iova_dma_addr(&host->iova, alloc); 217 175 job->unpins[job->num_unpins].size = gather_size; 176 + phys_addr = iova_dma_addr(&host->iova, alloc); 218 177 } else { 219 - job->addr_phys[job->num_unpins] = phys_addr; 178 + err = dma_map_sg(host->dev, sgt->sgl, sgt->nents, 179 + DMA_TO_DEVICE); 180 + if (!err) { 181 + err = -ENOMEM; 182 + goto unpin; 183 + } 184 + 185 + job->unpins[job->num_unpins].dev = host->dev; 186 + phys_addr = sg_dma_address(sgt->sgl); 220 187 } 221 188 222 - job->gather_addr_phys[i] = job->addr_phys[job->num_unpins]; 189 + job->addr_phys[job->num_unpins] = phys_addr; 190 + job->gather_addr_phys[i] = phys_addr; 223 191 192 + job->unpins[job->num_unpins].dir = DMA_TO_DEVICE; 224 193 job->unpins[job->num_unpins].bo = g->bo; 225 194 job->unpins[job->num_unpins].sgt = sgt; 226 195 job->num_unpins++; ··· 616 567 617 568 for (i = 0; i < job->num_unpins; i++) { 618 569 struct host1x_job_unpin_data *unpin = &job->unpins[i]; 570 + struct device *dev = unpin->dev ?: host->dev; 571 + struct sg_table *sgt = unpin->sgt; 619 572 620 573 if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && 621 574 unpin->size && host->domain) { ··· 627 576 iova_pfn(&host->iova, job->addr_phys[i])); 628 577 } 629 578 630 - host1x_bo_unpin(host->dev, unpin->bo, unpin->sgt); 579 + if (unpin->dev && sgt) 580 + dma_unmap_sg(unpin->dev, sgt->sgl, sgt->nents, 581 + unpin->dir); 582 + 583 + host1x_bo_unpin(dev, unpin->bo, sgt); 631 584 host1x_bo_put(unpin->bo); 632 585 } 633 586
+4
drivers/gpu/host1x/job.h
··· 8 8 #ifndef __HOST1X_JOB_H 9 9 #define __HOST1X_JOB_H 10 10 11 + #include <linux/dma-direction.h> 12 + 11 13 struct host1x_job_gather { 12 14 unsigned int words; 13 15 dma_addr_t base; ··· 21 19 struct host1x_job_unpin_data { 22 20 struct host1x_bo *bo; 23 21 struct sg_table *sgt; 22 + struct device *dev; 24 23 size_t size; 24 + enum dma_data_direction dir; 25 25 }; 26 26 27 27 /*