summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/host1x/cdma.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-05-05 11:47:01 +1000
committerDave Airlie <airlied@redhat.com>2017-05-05 11:47:01 +1000
commit644b4930bf7e2adeffbe842e1097f7933c6a9158 (patch)
treedcb9e56d30af9b590c511f89b0b6815dd67321e6 /drivers/gpu/host1x/cdma.c
parent8b03d1ed2c43a2ba5ef3381322ee4515b97381bf (diff)
parentb0d36daa0ab54714e05164f6e21d22f974a5eec1 (diff)
downloadlinux-644b4930bf7e2adeffbe842e1097f7933c6a9158.tar.bz2
Merge tag 'drm/tegra/for-4.12-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next
drm/tegra: Changes for v4.12-rc1 This contains various fixes to the host1x driver as well as a plug for a leak of kernel pointers to userspace. A fairly big addition this time around is the Video Image Composer (VIC) support that can be used to accelerate some 2D and image compositing operations. Furthermore the driver now supports FB modifiers, so we no longer rely on a custom IOCTL to set those. Finally this contains a few preparatory patches for Tegra186 support which unfortunately didn't quite make it this time, but will hopefully be ready for v4.13. * tag 'drm/tegra/for-4.12-rc1' of git://anongit.freedesktop.org/tegra/linux: gpu: host1x: Fix host1x driver shutdown gpu: host1x: Support module reset gpu: host1x: Sort includes alphabetically drm/tegra: Add VIC support dt-bindings: Add bindings for the Tegra VIC drm/tegra: Add falcon helper library drm/tegra: Add Tegra DRM allocation API drm/tegra: Add tiling FB modifiers drm/tegra: Don't leak kernel pointer to userspace drm/tegra: Protect IOMMU operations by mutex drm/tegra: Enable IOVA API when IOMMU support is enabled gpu: host1x: Add IOMMU support gpu: host1x: Fix potential out-of-bounds access iommu/iova: Fix compile error with CONFIG_IOMMU_IOVA=m iommu: Add dummy implementations for !IOMMU_IOVA MAINTAINERS: Add related headers to IOMMU section iommu/iova: Consolidate code for adding new node to iovad domain rbtree
Diffstat (limited to 'drivers/gpu/host1x/cdma.c')
-rw-r--r--drivers/gpu/host1x/cdma.c74
1 files changed, 58 insertions, 16 deletions
diff --git a/drivers/gpu/host1x/cdma.c b/drivers/gpu/host1x/cdma.c
index c5d82a8a2ec9..28541b280739 100644
--- a/drivers/gpu/host1x/cdma.c
+++ b/drivers/gpu/host1x/cdma.c
@@ -51,9 +51,15 @@ static void host1x_pushbuffer_destroy(struct push_buffer *pb)
struct host1x_cdma *cdma = pb_to_cdma(pb);
struct host1x *host1x = cdma_to_host1x(cdma);
- if (pb->phys != 0)
- dma_free_wc(host1x->dev, pb->size_bytes + 4, pb->mapped,
- pb->phys);
+ if (!pb->phys)
+ return;
+
+ if (host1x->domain) {
+ iommu_unmap(host1x->domain, pb->dma, pb->alloc_size);
+ free_iova(&host1x->iova, iova_pfn(&host1x->iova, pb->dma));
+ }
+
+ dma_free_wc(host1x->dev, pb->alloc_size, pb->mapped, pb->phys);
pb->mapped = NULL;
pb->phys = 0;
@@ -66,28 +72,64 @@ static int host1x_pushbuffer_init(struct push_buffer *pb)
{
struct host1x_cdma *cdma = pb_to_cdma(pb);
struct host1x *host1x = cdma_to_host1x(cdma);
+ struct iova *alloc;
+ u32 size;
+ int err;
pb->mapped = NULL;
pb->phys = 0;
- pb->size_bytes = HOST1X_PUSHBUFFER_SLOTS * 8;
+ pb->size = HOST1X_PUSHBUFFER_SLOTS * 8;
+
+ size = pb->size + 4;
/* initialize buffer pointers */
- pb->fence = pb->size_bytes - 8;
+ pb->fence = pb->size - 8;
pb->pos = 0;
- /* allocate and map pushbuffer memory */
- pb->mapped = dma_alloc_wc(host1x->dev, pb->size_bytes + 4, &pb->phys,
- GFP_KERNEL);
- if (!pb->mapped)
- goto fail;
+ if (host1x->domain) {
+ unsigned long shift;
+
+ size = iova_align(&host1x->iova, size);
+
+ pb->mapped = dma_alloc_wc(host1x->dev, size, &pb->phys,
+ GFP_KERNEL);
+ if (!pb->mapped)
+ return -ENOMEM;
+
+ shift = iova_shift(&host1x->iova);
+ alloc = alloc_iova(&host1x->iova, size >> shift,
+ host1x->iova_end >> shift, true);
+ if (!alloc) {
+ err = -ENOMEM;
+ goto iommu_free_mem;
+ }
+
+ pb->dma = iova_dma_addr(&host1x->iova, alloc);
+ err = iommu_map(host1x->domain, pb->dma, pb->phys, size,
+ IOMMU_READ);
+ if (err)
+ goto iommu_free_iova;
+ } else {
+ pb->mapped = dma_alloc_wc(host1x->dev, size, &pb->phys,
+ GFP_KERNEL);
+ if (!pb->mapped)
+ return -ENOMEM;
+
+ pb->dma = pb->phys;
+ }
+
+ pb->alloc_size = size;
host1x_hw_pushbuffer_init(host1x, pb);
return 0;
-fail:
- host1x_pushbuffer_destroy(pb);
- return -ENOMEM;
+iommu_free_iova:
+ __free_iova(&host1x->iova, alloc);
+iommu_free_mem:
+ dma_free_wc(host1x->dev, pb->alloc_size, pb->mapped, pb->phys);
+
+ return err;
}
/*
@@ -101,7 +143,7 @@ static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2)
WARN_ON(pb->pos == pb->fence);
*(p++) = op1;
*(p++) = op2;
- pb->pos = (pb->pos + 8) & (pb->size_bytes - 1);
+ pb->pos = (pb->pos + 8) & (pb->size - 1);
}
/*
@@ -111,7 +153,7 @@ static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2)
static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots)
{
/* Advance the next write position */
- pb->fence = (pb->fence + slots * 8) & (pb->size_bytes - 1);
+ pb->fence = (pb->fence + slots * 8) & (pb->size - 1);
}
/*
@@ -119,7 +161,7 @@ static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots)
*/
static u32 host1x_pushbuffer_space(struct push_buffer *pb)
{
- return ((pb->fence - pb->pos) & (pb->size_bytes - 1)) / 8;
+ return ((pb->fence - pb->pos) & (pb->size - 1)) / 8;
}
/*