diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index cc82410..38ec5e8 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -16,13 +16,7 @@
  */
 
 #include "msm_drv.h"
-#include "msm_mmu.h"
-
-struct msm_iommu {
-	struct msm_mmu base;
-	struct iommu_domain *domain;
-};
-#define to_msm_iommu(x) container_of(x, struct msm_iommu, base)
+#include "msm_iommu.h"
 
 static int msm_fault_handler(struct iommu_domain *domain, struct device *dev,
 		unsigned long iova, int flags, void *arg)
@@ -83,14 +77,19 @@ static int msm_iommu_v2_attach(struct msm_mmu *mmu, const char * const *names,
 			       int cnt)
 {
 	struct msm_iommu *iommu = to_msm_iommu(mmu);
-	int val = 1;
+	int val = 1, ret;
 
 	/* Use TTBR1 if it exists */
-	/* FIXME: This should only be for GPU and in theory only for A5XX */
-
-	iommu_domain_set_attr(iommu->domain, DOMAIN_ATTR_ENABLE_TTBR1,
+	ret = iommu_domain_set_attr(iommu->domain, DOMAIN_ATTR_ENABLE_TTBR1,
 		&val);
 
+	/*
+	 * We will only allow per-instance pagetables if TTBR1 is available
+	 * because the alternative is too labor intensive
+	 */
+	iommu->allow_dynamic = !ret ? true : false;
+
+	/* Attach the device to the domain */
 	return iommu_attach_device(iommu->domain, mmu->dev);
 }
 
@@ -102,6 +101,18 @@ static void msm_iommu_v2_detach(struct msm_mmu *mmu, const char * const *names,
 	iommu_detach_device(iommu->domain, mmu->dev);
 }
 
+/* Dynamic domains do not have attach/detach steps */
+static int msm_iommu_attach_dynamic(struct msm_mmu *mmu,
+		const char * const *names, int cnt)
+{
+	return 0;
+}
+
+static void msm_iommu_detach_dynamic(struct msm_mmu *mmu,
+				     const char * const *names, int cnt)
+{
+}
+
 static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova,
 		struct sg_table *sgt, unsigned len, int prot)
 {
@@ -193,24 +204,78 @@ static void msm_iommu_destroy(struct msm_mmu *mmu)
 		.destroy = msm_iommu_destroy,
 };
 
-struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain)
+/* These are for dynamic per-instance domains */
+static const struct msm_mmu_funcs dynamic_funcs = {
+		.attach = msm_iommu_attach_dynamic,
+		.detach = msm_iommu_detach_dynamic,
+		.map = msm_iommu_map,
+		.unmap = msm_iommu_unmap,
+		.destroy = msm_iommu_destroy,
+};
+
+struct msm_mmu *_msm_iommu_new(struct device *dev, struct iommu_domain *domain,
+		const struct msm_mmu_funcs *funcs)
 {
 	struct msm_iommu *iommu;
-	const struct msm_mmu_funcs *funcs;
 
 	iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
 	if (!iommu)
 		return ERR_PTR(-ENOMEM);
 
+	iommu->domain = domain;
+	msm_mmu_init(&iommu->base, dev, funcs);
+	iommu_set_fault_handler(domain, msm_fault_handler, iommu, true);
+
+	return &iommu->base;
+}
+
+struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain)
+{
+	const struct msm_mmu_funcs *funcs;
+
 	if (of_find_compatible_node(NULL, NULL, "qcom,msm-smmu-v2") ||
 			of_find_compatible_node(NULL, NULL, "qcom,msm-mmu-500"))
 		funcs = &funcs_v1;
 	else
 		funcs = &funcs_v2;
 
-	iommu->domain = domain;
-	msm_mmu_init(&iommu->base, dev, funcs);
-	iommu_set_fault_handler(domain, msm_fault_handler, iommu, true);
+	return _msm_iommu_new(dev, domain, funcs);
+}
 
-	return &iommu->base;
+/*
+ * Given a base domain that is attached to a IOMMU device try to create a
+ * dynamic domain that is also attached to the same device but allocates a new
+ * pagetable. This is used to allow multiple pagetables to be attached to the
+ * same device.
+ */
+struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base)
+{
+	static unsigned int procid;
+	struct msm_iommu *base_iommu = to_msm_iommu(base);
+	struct msm_iommu *iommu;
+	struct iommu_domain *domain;
+	struct msm_mmu *mmu;
+
+	/* Don't continue if the base domain didn't have the support we need */
+	if (!base || base_iommu->allow_dynamic == false)
+		return ERR_PTR(-EOPNOTSUPP);
+
+	domain = iommu_domain_create_dynamic(base_iommu->domain);
+	if (!domain)
+		return ERR_PTR(-ENODEV);
+
+	mmu = _msm_iommu_new(base->dev, domain, &dynamic_funcs);
+
+	if (IS_ERR(mmu)) {
+		if (domain)
+			iommu_domain_free(domain);
+	}
+
+	iommu = to_msm_iommu(mmu);
+
+	iommu_domain_get_attr(iommu->domain, DOMAIN_ATTR_TTBR0, &iommu->ttbr0);
+
+	iommu->contextidr = ++procid;
+
+	return mmu;
 }
diff --git a/drivers/gpu/drm/msm/msm_iommu.h b/drivers/gpu/drm/msm/msm_iommu.h
new file mode 100644
index 0000000..bfb7501
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_iommu.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_IOMMU_H_
+#define _MSM_IOMMU_H_
+
+#include "msm_mmu.h"
+
+struct msm_iommu {
+	struct msm_mmu base;
+	struct iommu_domain *domain;
+	int cb;
+	phys_addr_t ttbr0;
+	uint32_t contextidr;
+	bool allow_dynamic;
+};
+#define to_msm_iommu(x) container_of(x, struct msm_iommu, base)
+
+static inline bool msm_iommu_allow_dynamic(struct msm_mmu *mmu)
+{
+	struct msm_iommu *iommu = to_msm_iommu(mmu);
+
+	return iommu->allow_dynamic;
+}
+#endif
diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h
index e7d1967..65e5202 100644
--- a/drivers/gpu/drm/msm/msm_mmu.h
+++ b/drivers/gpu/drm/msm/msm_mmu.h
@@ -45,7 +45,7 @@ static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev,
 }
 
 struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain);
-struct msm_mmu *msm_gpummu_new(struct device *dev, struct msm_gpu *gpu);
+struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *orig);
 
 #ifdef CONFIG_QCOM_IOMMU_V1
 struct device *msm_iommu_get_ctx(const char *ctx_name);
