Blob Blame History Raw
From: Thierry Reding <treding@nvidia.com>
Date: Thu, 12 Oct 2017 17:43:33 +0200
Subject: drm/tegra: Use IOMMU groups
Git-commit: bc8828bd08bd2a645caeb64d299d67faca7a3b4f
Patch-mainline: v4.16-rc1
References: FATE#326289 FATE#326079 FATE#326049 FATE#322398 FATE#326166

In order to support IOMMUs more generically and transparently handle the
ARM SMMU on Tegra186, move to using groups instead of devices for domain
attachment. An IOMMU group is a set of devices that share the same IOMMU
domain and is therefore a good match to represent what Tegra DRM needs.

Signed-off-by: Thierry Reding <treding@nvidia.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 drivers/gpu/drm/tegra/dc.c  |   27 +++++++++++++++++----------
 drivers/gpu/drm/tegra/drm.h |    1 +
 drivers/gpu/drm/tegra/vic.c |   18 ++++++++++--------
 3 files changed, 28 insertions(+), 18 deletions(-)

--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1748,6 +1748,7 @@ static irqreturn_t tegra_dc_irq(int irq,
 static int tegra_dc_init(struct host1x_client *client)
 {
 	struct drm_device *drm = dev_get_drvdata(client->parent);
+	struct iommu_group *group = iommu_group_get(client->dev);
 	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
 	struct tegra_dc *dc = host1x_client_to_dc(client);
 	struct tegra_drm *tegra = drm->dev_private;
@@ -1759,12 +1760,17 @@ static int tegra_dc_init(struct host1x_c
 	if (!dc->syncpt)
 		dev_warn(dc->dev, "failed to allocate syncpoint\n");
 
-	if (tegra->domain) {
-		err = iommu_attach_device(tegra->domain, dc->dev);
-		if (err < 0) {
-			dev_err(dc->dev, "failed to attach to domain: %d\n",
-				err);
-			return err;
+	if (group && tegra->domain) {
+		if (group != tegra->group) {
+			err = iommu_attach_group(tegra->domain, group);
+			if (err < 0) {
+				dev_err(dc->dev,
+					"failed to attach to domain: %d\n",
+					err);
+				return err;
+			}
+
+			tegra->group = group;
 		}
 
 		dc->domain = tegra->domain;
@@ -1825,8 +1831,8 @@ cleanup:
 	if (!IS_ERR(primary))
 		drm_plane_cleanup(primary);
 
-	if (tegra->domain) {
-		iommu_detach_device(tegra->domain, dc->dev);
+	if (group && tegra->domain) {
+		iommu_detach_group(tegra->domain, group);
 		dc->domain = NULL;
 	}
 
@@ -1835,6 +1841,7 @@ cleanup:
 
 static int tegra_dc_exit(struct host1x_client *client)
 {
+	struct iommu_group *group = iommu_group_get(client->dev);
 	struct tegra_dc *dc = host1x_client_to_dc(client);
 	int err;
 
@@ -1846,8 +1853,8 @@ static int tegra_dc_exit(struct host1x_c
 		return err;
 	}
 
-	if (dc->domain) {
-		iommu_detach_device(dc->domain, dc->dev);
+	if (group && dc->domain) {
+		iommu_detach_group(dc->domain, group);
 		dc->domain = NULL;
 	}
 
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -60,6 +60,7 @@ struct tegra_drm {
 	struct drm_device *drm;
 
 	struct iommu_domain *domain;
+	struct iommu_group *group;
 	struct mutex mm_lock;
 	struct drm_mm mm;
 
--- a/drivers/gpu/drm/tegra/vic.c
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -138,13 +138,14 @@ static const struct falcon_ops vic_falco
 static int vic_init(struct host1x_client *client)
 {
 	struct tegra_drm_client *drm = host1x_to_drm_client(client);
+	struct iommu_group *group = iommu_group_get(client->dev);
 	struct drm_device *dev = dev_get_drvdata(client->parent);
 	struct tegra_drm *tegra = dev->dev_private;
 	struct vic *vic = to_vic(drm);
 	int err;
 
-	if (tegra->domain) {
-		err = iommu_attach_device(tegra->domain, vic->dev);
+	if (group && tegra->domain) {
+		err = iommu_attach_group(tegra->domain, group);
 		if (err < 0) {
 			dev_err(vic->dev, "failed to attach to domain: %d\n",
 				err);
@@ -158,13 +159,13 @@ static int vic_init(struct host1x_client
 		vic->falcon.data = tegra;
 		err = falcon_load_firmware(&vic->falcon);
 		if (err < 0)
-			goto detach_device;
+			goto detach;
 	}
 
 	vic->channel = host1x_channel_request(client->dev);
 	if (!vic->channel) {
 		err = -ENOMEM;
-		goto detach_device;
+		goto detach;
 	}
 
 	client->syncpts[0] = host1x_syncpt_request(client, 0);
@@ -183,9 +184,9 @@ free_syncpt:
 	host1x_syncpt_free(client->syncpts[0]);
 free_channel:
 	host1x_channel_put(vic->channel);
-detach_device:
-	if (tegra->domain)
-		iommu_detach_device(tegra->domain, vic->dev);
+detach:
+	if (group && tegra->domain)
+		iommu_detach_group(tegra->domain, group);
 
 	return err;
 }
@@ -193,6 +194,7 @@ detach_device:
 static int vic_exit(struct host1x_client *client)
 {
 	struct tegra_drm_client *drm = host1x_to_drm_client(client);
+	struct iommu_group *group = iommu_group_get(client->dev);
 	struct drm_device *dev = dev_get_drvdata(client->parent);
 	struct tegra_drm *tegra = dev->dev_private;
 	struct vic *vic = to_vic(drm);
@@ -206,7 +208,7 @@ static int vic_exit(struct host1x_client
 	host1x_channel_put(vic->channel);
 
 	if (vic->domain) {
-		iommu_detach_device(vic->domain, vic->dev);
+		iommu_detach_group(vic->domain, group);
 		vic->domain = NULL;
 	}