summaryrefslogtreecommitdiff
path: root/drivers/gpu/sprd_iommu
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/gpu/sprd_iommu
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/gpu/sprd_iommu')
-rw-r--r--drivers/gpu/sprd_iommu/Kconfig5
-rw-r--r--drivers/gpu/sprd_iommu/Makefile2
-rw-r--r--drivers/gpu/sprd_iommu/gsp/Makefile1
-rwxr-xr-xdrivers/gpu/sprd_iommu/gsp/sprd_iommu_gsp.c229
-rw-r--r--drivers/gpu/sprd_iommu/mm/Makefile1
-rw-r--r--drivers/gpu/sprd_iommu/mm/sprd_iommu_mm.c268
-rw-r--r--drivers/gpu/sprd_iommu/sprd_iommu_common.c349
-rw-r--r--drivers/gpu/sprd_iommu/sprd_iommu_common.h144
-rw-r--r--drivers/gpu/sprd_iommu/sprd_iommu_drv.c432
-rw-r--r--drivers/gpu/sprd_iommu/sprd_iommu_sysfs.c157
-rw-r--r--drivers/gpu/sprd_iommu/sprd_iommu_sysfs.h12
-rw-r--r--drivers/gpu/sprd_iommu/v2/Makefile6
-rw-r--r--drivers/gpu/sprd_iommu/v2/sprd_iommu_dcam.c192
-rw-r--r--drivers/gpu/sprd_iommu/v2/sprd_iommu_dispc.c229
-rw-r--r--drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp0.c229
-rw-r--r--drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp1.c229
-rw-r--r--drivers/gpu/sprd_iommu/v2/sprd_iommu_vpp.c192
-rw-r--r--drivers/gpu/sprd_iommu/v2/sprd_iommu_vsp.c192
18 files changed, 2869 insertions, 0 deletions
diff --git a/drivers/gpu/sprd_iommu/Kconfig b/drivers/gpu/sprd_iommu/Kconfig
new file mode 100644
index 00000000..b3fb2eaa
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/Kconfig
@@ -0,0 +1,5 @@
+config SPRD_IOMMU
+ bool "SPRD IOMMU Module"
+ help
+ Chose this option to enable the SPRD IOMMU.
+
diff --git a/drivers/gpu/sprd_iommu/Makefile b/drivers/gpu/sprd_iommu/Makefile
new file mode 100644
index 00000000..a51b6ffe
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_sysfs.o sprd_iommu_drv.o
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_common.o gsp/ mm/ v2/
diff --git a/drivers/gpu/sprd_iommu/gsp/Makefile b/drivers/gpu/sprd_iommu/gsp/Makefile
new file mode 100644
index 00000000..a3356432
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/gsp/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_gsp.o
diff --git a/drivers/gpu/sprd_iommu/gsp/sprd_iommu_gsp.c b/drivers/gpu/sprd_iommu/gsp/sprd_iommu_gsp.c
new file mode 100755
index 00000000..4d719372
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/gsp/sprd_iommu_gsp.c
@@ -0,0 +1,229 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include"../sprd_iommu_common.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void sprd_iommu_gsp_early_suspend(struct early_suspend *es)
+{
+ int err = -1;
+
+ struct sprd_iommu_dev *dev = container_of(es,
+ struct sprd_iommu_dev, early_suspend);
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_backup(dev);
+ mutex_unlock(&dev->mutex_map);
+}
+
+static void sprd_iommu_gsp_late_resume(struct early_suspend *es)
+{
+ int err = -1;
+
+ struct sprd_iommu_dev *dev = container_of(es,
+ struct sprd_iommu_dev, early_suspend);
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_restore(dev);
+ mutex_unlock(&dev->mutex_map);
+}
+#endif
+
+void sprd_iommu_gsp_clk_enable(struct sprd_iommu_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_prepare_enable(dev->mmu_mclock);
+ clk_prepare_enable(dev->mmu_clock);
+#endif
+}
+
+void sprd_iommu_gsp_clk_disable(struct sprd_iommu_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_disable_unprepare(dev->mmu_clock);
+ clk_disable_unprepare(dev->mmu_mclock);
+#endif
+}
+
+void sprd_iommu_gsp_open(struct sprd_iommu_dev *dev)
+{
+}
+
+void sprd_iommu_gsp_release(struct sprd_iommu_dev *dev)
+{
+}
+
+int sprd_iommu_gsp_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int err = -1;
+#ifndef CONFIG_SC_FPGA
+ struct device_node *np;
+
+ np = dev->misc_dev.this_device->of_node;
+ if (!np)
+ return -1;
+
+ dev->mmu_mclock = of_clk_get(np, 0);
+ dev->mmu_clock = of_clk_get(np, 2);
+
+ if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) {
+ pr_info("%s, can't get clock:%p, %p\n", __func__,
+ dev->mmu_clock, dev->mmu_mclock);
+ goto errorout;
+ }
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ dev->early_suspend.suspend = sprd_iommu_gsp_early_suspend;
+ dev->early_suspend.resume = sprd_iommu_gsp_late_resume;
+ dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING;
+ register_early_suspend(&dev->early_suspend);
+#endif
+
+ sprd_iommu_gsp_clk_enable(dev);
+ err = sprd_iommu_init(dev, data);
+ sprd_iommu_gsp_clk_disable(dev);
+
+ return err;
+
+#ifndef CONFIG_SC_FPGA
+errorout:
+ if (dev->mmu_clock)
+ clk_put(dev->mmu_clock);
+
+ if (dev->mmu_mclock)
+ clk_put(dev->mmu_mclock);
+
+ return -1;
+#endif
+}
+
+int sprd_iommu_gsp_exit(struct sprd_iommu_dev *dev)
+{
+ int err = -1;
+
+ sprd_iommu_gsp_clk_enable(dev);
+ err = sprd_iommu_exit(dev);
+ sprd_iommu_gsp_clk_disable(dev);
+
+ return err;
+}
+
+unsigned long sprd_iommu_gsp_iova_alloc(struct sprd_iommu_dev *dev,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_alloc(dev, iova_length);
+}
+
+void sprd_iommu_gsp_iova_free(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ sprd_iommu_iova_free(dev, iova, iova_length);
+}
+
+int sprd_iommu_gsp_iova_map(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length, struct sg_table *table)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count)
+ sprd_iommu_enable(dev);
+ dev->map_count++;
+
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_gsp_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+
+ dev->map_count--;
+ if (0 == dev->map_count)
+ sprd_iommu_disable(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_gsp_backup(struct sprd_iommu_dev *dev)
+{
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ return 0;
+#else
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_backup(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+#endif
+}
+
+int sprd_iommu_gsp_restore(struct sprd_iommu_dev *dev)
+{
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ return 0;
+#else
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_restore(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return 0;
+#endif
+}
+
+int sprd_iommu_gsp_dump(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_dump(dev, iova, iova_length);
+}
+
+struct sprd_iommu_ops iommu_gsp_ops = {
+ .init = sprd_iommu_gsp_init,
+ .exit = sprd_iommu_gsp_exit,
+ .iova_alloc = sprd_iommu_gsp_iova_alloc,
+ .iova_free = sprd_iommu_gsp_iova_free,
+ .iova_map = sprd_iommu_gsp_iova_map,
+ .iova_unmap = sprd_iommu_gsp_iova_unmap,
+ .backup = sprd_iommu_gsp_backup,
+ .restore = sprd_iommu_gsp_restore,
+ .enable = sprd_iommu_gsp_clk_enable,
+ .disable = sprd_iommu_gsp_clk_disable,
+ .dump = sprd_iommu_gsp_dump,
+ .open = sprd_iommu_gsp_open,
+ .release = sprd_iommu_gsp_release,
+};
+
diff --git a/drivers/gpu/sprd_iommu/mm/Makefile b/drivers/gpu/sprd_iommu/mm/Makefile
new file mode 100644
index 00000000..f49a0180
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/mm/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_mm.o
diff --git a/drivers/gpu/sprd_iommu/mm/sprd_iommu_mm.c b/drivers/gpu/sprd_iommu/mm/sprd_iommu_mm.c
new file mode 100644
index 00000000..c5ff6a24
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/mm/sprd_iommu_mm.c
@@ -0,0 +1,268 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include"../sprd_iommu_common.h"
+#include <linux/clk-provider.h>
+
+static void sprd_iommu_mm_prepare(struct sprd_iommu_dev *dev)
+{
+#ifndef CONFIG_SC_FPGA
+ pr_info("%s, axi clock:%p\n", __func__, dev->mmu_axiclock);
+ if (dev->mmu_axiclock)
+ clk_prepare(dev->mmu_axiclock);
+#endif
+}
+
+static void sprd_iommu_mm_unprepare(struct sprd_iommu_dev *dev)
+{
+#ifndef CONFIG_SC_FPGA
+ pr_info("%s, axi clock:%p\n", __func__, dev->mmu_axiclock);
+ if (dev->mmu_axiclock)
+ clk_unprepare(dev->mmu_axiclock);
+#endif
+}
+
+void sprd_iommu_mm_clk_enable(struct sprd_iommu_dev *dev)
+{
+ if (!dev->light_sleep_en)
+ pr_info("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_prepare_enable(dev->mmu_mclock);
+ clk_prepare_enable(dev->mmu_clock);
+#endif
+}
+
+void sprd_iommu_mm_clk_disable(struct sprd_iommu_dev *dev)
+{
+ if (!dev->light_sleep_en)
+ pr_info("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_disable_unprepare(dev->mmu_clock);
+ clk_disable_unprepare(dev->mmu_mclock);
+#endif
+}
+
+void sprd_iommu_mm_open(struct sprd_iommu_dev *dev)
+{
+}
+
+void sprd_iommu_mm_release(struct sprd_iommu_dev *dev)
+{
+}
+
+int sprd_iommu_mm_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int err = -1;
+#ifndef CONFIG_SC_FPGA
+ struct device_node *np;
+
+ np = dev->misc_dev.this_device->of_node;
+ if (!np)
+ return -1;
+
+ dev->mmu_clock = of_clk_get(np, 0);
+ dev->mmu_mclock = of_clk_get(np, 1);
+ dev->mmu_axiclock = of_clk_get(np, 2);
+ if (IS_ERR(dev->mmu_axiclock))
+ pr_info("%s, can't get mm axi clock:%p\n", __func__,
+ dev->mmu_axiclock);
+ else
+ dev->light_sleep_en = true;
+
+ if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) {
+ pr_info("%s, can't get clock:%p, %p\n", __func__,
+ dev->mmu_clock, dev->mmu_mclock);
+ goto errorout;
+ }
+#endif
+ sprd_iommu_mm_clk_enable(dev);
+ err = sprd_iommu_init(dev, data);
+ sprd_iommu_mm_clk_disable(dev);
+
+ return err;
+
+#ifndef CONFIG_SC_FPGA
+errorout:
+ if (dev->mmu_clock)
+ clk_put(dev->mmu_clock);
+
+ if (dev->mmu_mclock)
+ clk_put(dev->mmu_mclock);
+
+ if (dev->mmu_axiclock)
+ clk_put(dev->mmu_axiclock);
+
+ return -1;
+#endif
+}
+
+int sprd_iommu_mm_exit(struct sprd_iommu_dev *dev)
+{
+ int err = -1;
+
+ sprd_iommu_mm_clk_enable(dev);
+ err = sprd_iommu_exit(dev);
+ sprd_iommu_mm_clk_disable(dev);
+
+ return err;
+}
+
+unsigned long sprd_iommu_mm_iova_alloc(struct sprd_iommu_dev *dev,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_alloc(dev, iova_length);
+}
+
+void sprd_iommu_mm_iova_free(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ sprd_iommu_iova_free(dev, iova, iova_length);
+}
+
+int sprd_iommu_mm_iova_map(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length, struct sg_table *table)
+{
+ int err = -1;
+
+ if (dev->light_sleep_en) {
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count)
+ sprd_iommu_mm_prepare(dev);
+
+ sprd_iommu_mm_clk_enable(dev);
+
+ if (0 == dev->map_count)
+ sprd_iommu_reset_enable(dev);
+ dev->map_count++;
+
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ sprd_iommu_mm_clk_disable(dev);
+ mutex_unlock(&dev->mutex_map);
+ } else {
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count) {
+ sprd_iommu_mm_clk_enable(dev);
+ sprd_iommu_reset_enable(dev);
+ }
+ dev->map_count++;
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ mutex_unlock(&dev->mutex_map);
+ }
+ return err;
+}
+
+int sprd_iommu_mm_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ int err = -1;
+
+ if (dev->light_sleep_en) {
+ mutex_lock(&dev->mutex_map);
+ sprd_iommu_mm_clk_enable(dev);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+
+ dev->map_count--;
+ if (0 == dev->map_count)
+ sprd_iommu_disable(dev);
+
+ sprd_iommu_mm_clk_disable(dev);
+
+ if (0 == dev->map_count)
+ sprd_iommu_mm_unprepare(dev);
+ mutex_unlock(&dev->mutex_map);
+ } else {
+ mutex_lock(&dev->mutex_map);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+ dev->map_count--;
+ if (0 == dev->map_count) {
+ sprd_iommu_disable(dev);
+ sprd_iommu_mm_clk_disable(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+ }
+ return err;
+}
+
+int sprd_iommu_mm_backup(struct sprd_iommu_dev *dev)
+{
+ int err = 0;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+
+ if (dev->light_sleep_en) {
+ if (dev->map_count > 0) {
+ sprd_iommu_mm_clk_enable(dev);
+ err = sprd_iommu_backup(dev);
+ sprd_iommu_mm_clk_disable(dev);
+ sprd_iommu_mm_unprepare(dev);
+ }
+ } else {
+ if (dev->map_count > 0) {
+ err = sprd_iommu_backup(dev);
+ sprd_iommu_mm_clk_disable(dev);
+ }
+ }
+ mutex_unlock(&dev->mutex_map);
+ return err;
+}
+
+int sprd_iommu_mm_restore(struct sprd_iommu_dev *dev)
+{
+ int err = 0;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+
+ if (dev->light_sleep_en) {
+ if (dev->map_count > 0) {
+ sprd_iommu_mm_prepare(dev);
+ sprd_iommu_mm_clk_enable(dev);
+ err = sprd_iommu_restore(dev);
+ sprd_iommu_mm_clk_disable(dev);
+ }
+ } else {
+ if (dev->map_count > 0) {
+ sprd_iommu_mm_clk_enable(dev);
+ err = sprd_iommu_restore(dev);
+ }
+ }
+ mutex_unlock(&dev->mutex_map);
+ return err;
+}
+
+int sprd_iommu_mm_dump(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_dump(dev, iova, iova_length);
+}
+
+struct sprd_iommu_ops iommu_mm_ops = {
+ .init = sprd_iommu_mm_init,
+ .exit = sprd_iommu_mm_exit,
+ .iova_alloc = sprd_iommu_mm_iova_alloc,
+ .iova_free = sprd_iommu_mm_iova_free,
+ .iova_map = sprd_iommu_mm_iova_map,
+ .iova_unmap = sprd_iommu_mm_iova_unmap,
+ .backup = sprd_iommu_mm_backup,
+ .restore = sprd_iommu_mm_restore,
+ .enable = sprd_iommu_mm_clk_enable,
+ .disable = sprd_iommu_mm_clk_disable,
+ .dump = sprd_iommu_mm_dump,
+ .open = sprd_iommu_mm_open,
+ .release = sprd_iommu_mm_release,
+};
+
diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_common.c b/drivers/gpu/sprd_iommu/sprd_iommu_common.c
new file mode 100644
index 00000000..0092bf61
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/sprd_iommu_common.c
@@ -0,0 +1,349 @@
+#include "sprd_iommu_common.h"
+
+inline void mmu_reg_write(ulong reg, ulong val, ulong msk)
+{
+ __raw_writel((__raw_readl((void *)reg) & ~msk) | val, (void *)reg);
+}
+
+static inline void __set_pte(struct sprd_iommu_init_data *data,
+ ulong base, u32 offset, ulong paddr)
+{
+ /*64-bit virtual addr and 32bit phy addr in arm64.*/
+ if (data->iommu_rev == 2)
+ ((u32 *)base)[offset] = (u32)(paddr >> 12);
+ else
+ ((u32 *)base)[offset] = (u32)paddr;
+}
+
+ulong get_phys_addr(struct scatterlist *sg)
+{
+ /*
+ * Try sg_dma_address first so that we can
+ * map carveout regions that do not have a
+ * struct page associated with them.
+ */
+ ulong pa = sg_dma_address(sg);
+ if (pa == 0)
+ pa = sg_phys(sg);
+ return pa;
+}
+
+static void sprd_iommu_pgt_reset(struct sprd_iommu_init_data *data)
+{
+ ulong fault_page = data->fault_page;
+ int i;
+
+ if (fault_page) {
+ for (i = 0; i < data->pgt_size / sizeof(u32); i++)
+ __set_pte(data, data->pgt_base, i, fault_page);
+ } else {
+ memset((void *)data->pgt_base, 0xFF, data->pgt_size);
+ }
+}
+
+static inline void sprd_iommu_pgt_update(struct sprd_iommu_init_data *data,
+ ulong iova, ulong pa, size_t size)
+{
+ ulong end_iova = iova + size;
+ for (; iova < end_iova; iova += MMU_PAGE_SIZE, pa += MMU_PAGE_SIZE) {
+ __set_pte(data, data->pgt_base, MMU_PTE_OFFSET(iova), pa);
+ }
+}
+
+static inline void sprd_iommu_pgt_clear(struct sprd_iommu_init_data *data,
+ ulong iova, size_t size)
+{
+ ulong pgt_start = (ulong)((u32 *)data->pgt_base + MMU_PTE_OFFSET(iova));
+
+ pr_debug("%s, iova:0x%lx, size:0x%zx, fault_page:0x%lx\n", __func__,
+ iova, size, data->fault_page);
+
+ if (data->fault_page) {
+ ulong end_iova = iova + size;
+ ulong fault_page = data->fault_page;
+
+ for (; iova < end_iova; iova += MMU_PAGE_SIZE)
+ __set_pte(data, data->pgt_base, MMU_PTE_OFFSET(iova),
+ fault_page);
+ } else {
+ if (pgt_start % sizeof(ulong)) {
+ *((u32 *)pgt_start) = (u32)-1;
+ memset((void *)((u32 *)pgt_start + 1), 0xFF,
+ MMU_IOVA_SIZE_TO_PTE(size - MMU_PAGE_SIZE));
+ } else {
+ memset((void *)pgt_start, 0xFF,
+ MMU_IOVA_SIZE_TO_PTE(size));
+ }
+ }
+
+}
+
+static void sprd_iommu_reg_init(struct sprd_iommu_init_data *data)
+{
+ if (data->re_route_page) {
+ mmu_reg_write(data->ctrl_reg + OFFSET_RR_DEST_HI,
+ (u32)(data->re_route_page >> 32),
+ MMU_START_MB_ADDR_HI_MASK);
+ mmu_reg_write(data->ctrl_reg + OFFSET_RR_DEST_LO,
+ (u32)(data->re_route_page),
+ MMU_START_MB_ADDR_LO_MASK);
+ mmu_reg_write(data->ctrl_reg + OFFSET_CFG,
+ MMU_RR_EN(1),
+ MMU_RR_EN_MASK);
+ }
+
+ if (data->iommu_rev == 2) {
+ mmu_reg_write(data->ctrl_reg + OFFSET_START_MB_ADDR_HI,
+ (u32)(data->iova_base >> 32),
+ MMU_START_MB_ADDR_HI_MASK);
+ mmu_reg_write(data->ctrl_reg + OFFSET_START_MB_ADDR_LO,
+ (u32)(data->iova_base),
+ MMU_START_MB_ADDR_LO_MASK);
+
+ mmu_reg_write(data->ctrl_reg + OFFSET_CFG,
+ MMU_MON_EN(1),
+ MMU_MON_EN_MASK);
+ } else {
+#ifdef CONFIG_ARCH_SCX35L
+ mmu_reg_write(data->ctrl_reg,
+ MMU_V1_RAMCLK_DIV2_EN(1),
+ MMU_V1_RAMCLK_DIV2_EN_MASK);
+#endif
+ mmu_reg_write(data->ctrl_reg,
+ data->iova_base,
+ MMU_START_MB_ADDR_MASK);
+ }
+
+ mmu_reg_write(data->ctrl_reg, MMU_TLB_EN(1), MMU_TLB_EN_MASK);
+ mmu_reg_write(data->ctrl_reg, MMU_EN(1), MMU_EN_MASK);
+}
+
+static void sprd_iommu_reg_clear(struct sprd_iommu_init_data *data)
+{
+ if (data->iommu_rev == 2) {
+ mmu_reg_write(data->ctrl_reg + OFFSET_CFG,
+ MMU_RR_EN(0),
+ MMU_RR_EN_MASK);
+ mmu_reg_write(data->ctrl_reg + OFFSET_CFG,
+ MMU_MON_EN(0),
+ MMU_MON_EN_MASK);
+ } else {
+ mmu_reg_write(data->ctrl_reg,
+ MMU_V1_RAMCLK_DIV2_EN(0),
+ MMU_V1_RAMCLK_DIV2_EN_MASK);
+ }
+
+ mmu_reg_write(data->ctrl_reg, MMU_TLB_EN(0), MMU_TLB_EN_MASK);
+ mmu_reg_write(data->ctrl_reg, MMU_EN(0), MMU_EN_MASK);
+}
+
+int sprd_iommu_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int i;
+
+ pr_info("%s, %s, id:%d, iova_base:0x%lx, size:0x%zx\n", __func__,
+ data->name, data->id, data->iova_base, data->iova_size);
+
+ pr_info("pgt_base:0x%lx,size:0x%zx, fault_page:0x%lx, rr_page:0x%lx\n",
+ data->pgt_base, data->pgt_size, data->fault_page,
+ data->re_route_page);
+
+ if (data->fault_page) {
+ void *p = __va(data->fault_page);
+ pr_info("fault_page va: %p", p);
+ for (i = 0; i < PAGE_SIZE / sizeof(u32); i++)
+ ((u32 *)p)[i] = 0xDEADDEAD;
+ }
+ sprd_iommu_pgt_reset(data);
+
+ dev->pgt = __get_free_pages(GFP_KERNEL, get_order(data->pgt_size));
+ if (!dev->pgt) {
+ pr_err("%s get_free_pages fail\n", data->name);
+ return -1;
+ }
+
+ dev->pool = gen_pool_create(12, -1);
+ if (!dev->pool) {
+ free_pages((ulong)dev->pgt, get_order(data->pgt_size));
+ pr_err("%s gen_pool_create error\n", data->name);
+ return -1;
+ }
+ gen_pool_add(dev->pool, data->iova_base, data->iova_size, -1);
+
+ if (data->re_route_page) {
+ void *p = __va(data->re_route_page);
+ pr_info("re_route_page va: %p", p);
+ for (i = 0; i < PAGE_SIZE / sizeof(u32); i++)
+ ((u32 *)p)[i] = 0xDEADDEAD;
+ }
+
+ sprd_iommu_reg_init(data);
+
+ if (data->iommu_rev == 2) {
+ pr_info("ctrl_reg: 0x%x,cfg_reg: 0x%x,start_mb_addr:0x%x 0x%x",
+ __raw_readl((void *)(data->ctrl_reg)),
+ __raw_readl((void *)(data->ctrl_reg + OFFSET_CFG)),
+ __raw_readl((void *)(data->ctrl_reg + OFFSET_START_MB_ADDR_HI)),
+ __raw_readl((void *)(data->ctrl_reg + OFFSET_START_MB_ADDR_LO)));
+
+ pr_info("param_reg: 0x%x, rev_reg: 0x%x",
+ __raw_readl((void *)(data->ctrl_reg + OFFSET_PARAM)),
+ __raw_readl((void *)(data->ctrl_reg + OFFSET_REV)));
+ }
+
+ return 0;
+}
+
+int sprd_iommu_exit(struct sprd_iommu_dev *dev)
+{
+ sprd_iommu_reg_clear(dev->init_data);
+ gen_pool_destroy(dev->pool);
+ free_pages(dev->pgt, get_order(dev->init_data->pgt_size));
+ dev->pgt = 0;
+ return 0;
+}
+
+ulong sprd_iommu_iova_alloc(struct sprd_iommu_dev *dev, size_t iova_length)
+{
+ ulong iova = 0;
+
+ if (0 == iova_length) {
+ pr_err("%s, %s, iova_length:0x%zx\n", __func__,
+ dev->init_data->name, iova_length);
+ return 0;
+ }
+
+ iova = gen_pool_alloc(dev->pool, iova_length);
+ pr_debug("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__,
+ dev->init_data->name, iova, iova_length);
+ if (0 == iova) {
+ pr_err("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__,
+ dev->init_data->name, iova, iova_length);
+ }
+
+ return iova;
+}
+
+void sprd_iommu_iova_free(struct sprd_iommu_dev *dev,
+ ulong iova, size_t iova_length)
+{
+ struct sprd_iommu_init_data *data = dev->init_data;
+
+ if (((data->iova_base + data->iova_size) < (iova + iova_length))
+ || (data->iova_base > iova) || (0 == iova) || (0 == iova_length)) {
+ pr_err("%s, %s, iova_base:0x%lx, iova_size:0x%zx\n",
+ __func__, data->name, data->iova_base, data->iova_size);
+ pr_err("iova:0x%lx, len:0x%zx\n", iova, iova_length);
+ return;
+ }
+ gen_pool_free(dev->pool, iova, iova_length);
+ pr_debug("%s, %s, iova:0x%lx, len:0x%zx\n", __func__,
+ data->name, iova, iova_length);
+}
+
+int sprd_iommu_iova_map(struct sprd_iommu_dev *dev, ulong iova,
+ size_t iova_length, struct sg_table *table)
+{
+ struct scatterlist *sg;
+ int i = 0;
+ ulong iova_cur = 0;
+
+ if ((0 == iova) || (0 == iova_length)) {
+ pr_err("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__,
+ dev->init_data->name, iova, iova_length);
+ return -1;
+ }
+ iova_cur=iova;
+ mutex_lock(&dev->mutex_pgt);
+ /*disable TLB enable in MMU ctrl register*/
+ mmu_reg_write(dev->init_data->ctrl_reg,MMU_TLB_EN(0),MMU_TLB_EN_MASK);
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ sprd_iommu_pgt_update(dev->init_data, iova_cur,
+ get_phys_addr(sg), PAGE_ALIGN(sg->length));
+ iova_cur += PAGE_ALIGN(sg->length);
+ if (iova_cur >= iova + iova_length)
+ break;
+ }
+ mmu_reg_write(dev->init_data->ctrl_reg,MMU_TLB_EN(1),MMU_TLB_EN_MASK);
+ mutex_unlock(&dev->mutex_pgt);
+
+ pr_debug("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__,
+ dev->init_data->name, iova, iova_length);
+
+ return 0;
+}
+
+int sprd_iommu_iova_unmap(struct sprd_iommu_dev *dev, ulong iova,
+ size_t iova_length)
+{
+ if ((0 == iova) || (0 == iova_length)) {
+ pr_err("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__,
+ dev->init_data->name, iova, iova_length);
+ return -1;
+ }
+
+ mutex_lock(&dev->mutex_pgt);
+ /*disable TLB enable in MMU ctrl register*/
+ mmu_reg_write(dev->init_data->ctrl_reg,MMU_TLB_EN(0),MMU_TLB_EN_MASK);
+ sprd_iommu_pgt_clear(dev->init_data, iova, iova_length);
+ mmu_reg_write(dev->init_data->ctrl_reg,MMU_TLB_EN(1),MMU_TLB_EN_MASK);
+ mutex_unlock(&dev->mutex_pgt);
+
+ pr_debug("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__,
+ dev->init_data->name, iova, iova_length);
+
+ return 0;
+}
+
+int sprd_iommu_backup(struct sprd_iommu_dev *dev)
+{
+ mutex_lock(&dev->mutex_pgt);
+ memcpy((ulong *)dev->pgt, (ulong *)dev->init_data->pgt_base,
+ dev->init_data->pgt_size);
+ sprd_iommu_reg_clear(dev->init_data);
+ mutex_unlock(&dev->mutex_pgt);
+
+ return 0;
+}
+
+int sprd_iommu_restore(struct sprd_iommu_dev *dev)
+{
+ mutex_lock(&dev->mutex_pgt);
+ mmu_reg_write(dev->init_data->ctrl_reg, MMU_EN(0), MMU_EN_MASK);
+ memcpy((ulong *)dev->init_data->pgt_base, (ulong *)dev->pgt,
+ dev->init_data->pgt_size);
+ sprd_iommu_reg_init(dev->init_data);
+ mutex_unlock(&dev->mutex_pgt);
+
+ return 0;
+}
+
+void sprd_iommu_disable(struct sprd_iommu_dev *dev)
+{
+ mutex_lock(&dev->mutex_pgt);
+ sprd_iommu_reg_clear(dev->init_data);
+ mutex_unlock(&dev->mutex_pgt);
+}
+
+void sprd_iommu_enable(struct sprd_iommu_dev *dev)
+{
+ mutex_lock(&dev->mutex_pgt);
+ mmu_reg_write(dev->init_data->ctrl_reg, MMU_EN(0), MMU_EN_MASK);
+ sprd_iommu_reg_init(dev->init_data);
+ mutex_unlock(&dev->mutex_pgt);
+}
+
+void sprd_iommu_reset_enable(struct sprd_iommu_dev *dev)
+{
+ mutex_lock(&dev->mutex_pgt);
+ mmu_reg_write(dev->init_data->ctrl_reg, MMU_EN(0), MMU_EN_MASK);
+ sprd_iommu_pgt_reset(dev->init_data);
+ sprd_iommu_reg_init(dev->init_data);
+ mutex_unlock(&dev->mutex_pgt);
+}
+
+int sprd_iommu_dump(struct sprd_iommu_dev *dev, ulong iova, size_t iova_length)
+{
+ return 0;
+}
diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_common.h b/drivers/gpu/sprd_iommu/sprd_iommu_common.h
new file mode 100644
index 00000000..12dd0cad
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/sprd_iommu_common.h
@@ -0,0 +1,144 @@
+#ifndef __SPRD_IOMMU_COMMON_H__
+#define __SPRD_IOMMU_COMMON_H__
+
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/genalloc.h>
+#include <linux/sprd_iommu.h>
+#include <linux/scatterlist.h>
+#include <linux/of.h>
+
+#define OFFSET_CFG 0x4
+#define OFFSET_START_MB_ADDR_LO 0x8
+#define OFFSET_START_MB_ADDR_HI 0xC
+#define OFFSET_RR_DEST_LO 0x10
+#define OFFSET_RR_DEST_HI 0x14
+#define OFFSET_OFR_ADDR_LO_R 0x100
+#define OFFSET_OFR_ADDR_HI_R 0x104
+#define OFFSET_TRANS_CNT_R 0x108
+#define OFFSET_PT_MISS_CNT_R 0x10C
+#define OFFSET_TLB_MISS_CNT_R 0x110
+#define OFFSET_OFR_ADDR_LO_W 0x114
+#define OFFSET_OFR_ADDR_HI_W 0x118
+#define OFFSET_TRANS_CNT_W 0x11C
+#define OFFSET_PT_MISS_CNT_W 0x120
+#define OFFSET_TLB_MISS_CNT_W 0x124
+#define OFFSET_AXI_STAT 0x128
+#define OFFSET_FSM_STAT 0x12C
+#define OFFSET_PARAM 0x3F8
+#define OFFSET_REV 0x3FC
+
+#define AC(X, Y) (X##Y)
+
+/*common define*/
+/*MMU_CTRL register*/
+#define MMU_EN(_x_) ((AC(_x_, UL)) << 0 & (BIT(0)))
+#define MMU_EN_MASK (BIT(0))
+#define MMU_TLB_EN(_x_) ((AC(_x_, UL)) << 1 & (BIT(1)))
+#define MMU_TLB_EN_MASK (BIT(1))
+
+/*IOMMU rev1 define*/
+#define MMU_V1_RAMCLK_DIV2_EN(_x_) ((AC(_x_, UL)) << 2 & (BIT(2)))
+#define MMU_V1_RAMCLK_DIV2_EN_MASK (BIT(2))
+#define MMU_START_MB_ADDR_MASK (BIT(20)|BIT(21)|BIT(22)|BIT(23)|BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31))
+
+/*IOMMU rev2 define*/
+/*MMU_CFG register*/
+#define MMU_RAMCLK_DIV2_EN(_x_) ((AC(_x_, UL)) << 0 & (BIT(0)))
+#define MMU_RAMCLK_DIV2_EN_MASK (BIT(0))
+#define MMU_TLB_RP_R(_x_) ((AC(_x_, UL)) << 4 & (BIT(4)))
+#define MMU_TLB_RP_R_MASK (BIT(4))
+#define MMU_TLB_RP_W(_x_) ((AC(_x_, UL)) << 5 & (BIT(5)))
+#define MMU_TLB_RP_W_MASK (BIT(5))
+#define MMU_RR_EN(_x_) ((AC(_x_, UL)) << 6 & (BIT(6)))
+#define MMU_RR_EN_MASK (BIT(6))
+#define MMU_MON_EN(_x_) ((AC(_x_, UL)) << 7 & (BIT(7)))
+#define MMU_MON_EN_MASK (BIT(7))
+
+/*MMU_START_MB_ADDR registers*/
+#define MMU_START_MB_ADDR_LO_MASK (BIT(20)|BIT(21)|BIT(22)|BIT(23)|BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31))
+#define MMU_START_MB_ADDR_HI_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
+
+/*MMU_RR_DEST registers*/
+#define MMU_RR_DEST_LO_MASK (BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)|BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)|BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31))
+#define MMU_RR_DEST_HI_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
+
+/*MMU_OFR_ADDR_R registers*/
+#define MMU_OFR_ADDR_LO_R_MASK (0xFFFFFFFFUL)
+#define MMU_OFR_ADDR_HI_R_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
+
+/*MMU CNT_R registers*/
+#define MMU_TRANS_CNT_R_MASK (0xFFFFFFFFUL)
+#define MMU_PT_MISS_CNT_R_MASK (0xFFFFFFFFUL)
+#define MMU_TLB_MISS_CNT_R_MASK (0xFFFFFFFFUL)
+
+/*MMU_OFR_ADDR_W register*/
+#define MMU_OFR_ADDR_LO_W_MASK (0xFFFFFFFFUL)
+#define MMU_OFR_ADDR_HI_W_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
+
+/*MMU CNT_W registers*/
+#define MMU_TRANS_CNT_W_MASK (0xFFFFFFFFUL)
+#define MMU_PT_MISS_CNT_W_MASK (0xFFFFFFFFUL)
+#define MMU_TLB_MISS_CNT_W_MASK (0xFFFFFFFFUL)
+
+/*MMU_PARAM registers*/
+#define MMU_PARAM_AXI_ABW_MASK (BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29))
+#define MMU_PARAM_TLB_SIZE_MASK (BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23))
+#define MMU_PARAM_PGAE_SIZE_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
+#define MMU_PARAM_PT_SIZE_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4))
+
+/*MMU_REV registers*/
+#define MMU_MAJOR_REV_MASK (BIT(4)|BIT(5)|BIT(6)|BIT(7))
+#define MMU_MINOR_REV_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+
+
+#define MMU_PAGE_SIZE PAGE_SIZE /*0x1000 need check*/
+/**
+ * Page table index from address
+ * Calculates the page table index from the given address
+*/
+#define MMU_PTE_OFFSET(address) (((address)>>12) & 0xFFFF)
+
+/*64-bit virtual addr and 32bit phy addr in arm 64-bit of TSharkL.
+ *So we use (sizeof(unsigned int)).
+ */
+#define MMU_IOVA_SIZE_TO_PTE(size) ((sizeof(unsigned int))*(size)/MMU_PAGE_SIZE)
+
+extern const struct of_device_id iommu_ids[];
+
+void mmu_reg_write(unsigned long reg, unsigned long val, unsigned long msk);
+
+int sprd_iommu_init(struct sprd_iommu_dev *dev, struct sprd_iommu_init_data *data);
+
+int sprd_iommu_exit(struct sprd_iommu_dev *dev);
+
+unsigned long sprd_iommu_iova_alloc(struct sprd_iommu_dev *dev, size_t iova_length);
+
+void sprd_iommu_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, size_t iova_length);
+
+int sprd_iommu_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, size_t iova_length, struct sg_table *table);
+
+int sprd_iommu_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, size_t iova_length);
+
+int sprd_iommu_backup(struct sprd_iommu_dev *dev);
+
+int sprd_iommu_restore(struct sprd_iommu_dev *dev);
+
+void sprd_iommu_disable(struct sprd_iommu_dev *dev);
+
+void sprd_iommu_enable(struct sprd_iommu_dev *dev);
+
+void sprd_iommu_reset_enable(struct sprd_iommu_dev *dev);
+
+int sprd_iommu_dump(struct sprd_iommu_dev *dev, unsigned long iova, size_t iova_length);
+
+#endif
diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_drv.c b/drivers/gpu/sprd_iommu/sprd_iommu_drv.c
new file mode 100644
index 00000000..b4acf820
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/sprd_iommu_drv.c
@@ -0,0 +1,432 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/clk.h>
+#include <linux/miscdevice.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/genalloc.h>
+#include <linux/sprd_iommu.h>
+#include <linux/scatterlist.h>
+#include <asm/io.h>
+#include "sprd_iommu_sysfs.h"
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+static int sprd_iommu_probe(struct platform_device *pdev);
+static int sprd_iommu_remove(struct platform_device *pdev);
+static int sprd_iommu_suspend(struct platform_device *pdev, pm_message_t state);
+static int sprd_iommu_resume(struct platform_device *pdev);
+
+struct sprd_iommu_dev *iommu_devs[IOMMU_MAX] = {NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL};
+
+static struct sprd_iommu_init_data sprd_iommu_gsp_data = {
+ .id = IOMMU_GSP,
+ .name = "sprd_iommu_gsp",
+};
+
+static struct sprd_iommu_init_data sprd_iommu_mm_data = {
+ .id = IOMMU_MM,
+ .name = "sprd_iommu_mm",
+};
+
+static struct sprd_iommu_init_data sprd_iommu_vsp_data = {
+ .id = IOMMU_VSP,
+ .name = "sprd_iommu_vsp",
+};
+
+static struct sprd_iommu_init_data sprd_iommu_dcam_data = {
+ .id = IOMMU_DCAM,
+ .name = "sprd_iommu_dcam",
+};
+
+static struct sprd_iommu_init_data sprd_iommu_dispc_data = {
+ .id = IOMMU_DISPC,
+ .name = "sprd_iommu_dispc",
+};
+
+static struct sprd_iommu_init_data sprd_iommu_gsp0_data = {
+ .id = IOMMU_GSP0,
+ .name = "sprd_iommu_gsp0",
+};
+
+static struct sprd_iommu_init_data sprd_iommu_gsp1_data = {
+ .id = IOMMU_GSP1,
+ .name = "sprd_iommu_gsp1",
+};
+
+static struct sprd_iommu_init_data sprd_iommu_vpp_data = {
+ .id = IOMMU_VPP,
+ .name = "sprd_iommu_vpp",
+};
+
+const struct of_device_id iommu_ids[] = {
+ { .compatible = "sprd,sprd_iommu", },
+ {},
+};
+
+static struct platform_driver iommu_driver = {
+ .probe = sprd_iommu_probe,
+ .remove = sprd_iommu_remove,
+#ifndef CONFIG_ARCH_SCX35L
+ .suspend = sprd_iommu_suspend,
+ .resume = sprd_iommu_resume,
+#endif
+ .driver = {
+ .owner=THIS_MODULE,
+ .name="sprd_iommu",
+ .of_match_table = iommu_ids,
+ },
+};
+
+unsigned long sprd_iova_alloc(int iommu_id, unsigned long iova_length)
+{
+ if (iommu_devs[iommu_id]) {
+ return iommu_devs[iommu_id]->ops->iova_alloc(
+ iommu_devs[iommu_id], iova_length);
+ } else {
+ pr_err("%s, id:%d iommu don't exist, error!",
+ __func__, iommu_id);
+ return 0;
+ }
+}
+
+void sprd_iova_free(int iommu_id, unsigned long iova,
+ unsigned long iova_length)
+{
+ if (iommu_devs[iommu_id]) {
+ iommu_devs[iommu_id]->ops->iova_free(
+ iommu_devs[iommu_id], iova, iova_length);
+ } else {
+ pr_err("%s, id:%d iommu don't exist, error!",
+ __func__, iommu_id);
+ }
+}
+
+int sprd_iova_map(int iommu_id, unsigned long iova, unsigned long iova_length,
+ struct sg_table *table)
+{
+ if (iommu_devs[iommu_id]) {
+ return iommu_devs[iommu_id]->ops->iova_map(
+ iommu_devs[iommu_id], iova, iova_length, table);
+ } else {
+ pr_err("%s, id:%d iommu don't exist, error!",
+ __func__, iommu_id);
+ return -1;
+ }
+}
+
+int sprd_iova_unmap(int iommu_id, unsigned long iova,
+ unsigned long iova_length)
+{
+ if (iommu_devs[iommu_id]) {
+ return iommu_devs[iommu_id]->ops->iova_unmap(
+ iommu_devs[iommu_id], iova, iova_length);
+ } else {
+ pr_err("%s, id:%d iommu don't exist, error!",
+ __func__, iommu_id);
+ return -1;
+ }
+}
+
+void sprd_iommu_module_enable(uint32_t iommu_id)
+{
+ if (iommu_devs[iommu_id]->light_sleep_en ||
+ IOMMU_GSP == iommu_id ||
+ IOMMU_DISPC == iommu_id ||
+ IOMMU_GSP0 == iommu_id ||
+ IOMMU_GSP1 == iommu_id) {
+ if (iommu_devs[iommu_id])
+ iommu_devs[iommu_id]->ops->enable(iommu_devs[iommu_id]);
+ else
+ pr_err("%s, id:%d iommu don't exist, error!",
+ __func__, iommu_id);
+ }
+}
+
+void sprd_iommu_module_disable(uint32_t iommu_id)
+{
+ if (iommu_devs[iommu_id]->light_sleep_en ||
+ IOMMU_GSP == iommu_id ||
+ IOMMU_DISPC == iommu_id ||
+ IOMMU_GSP0 == iommu_id ||
+ IOMMU_GSP1 == iommu_id) {
+ if (iommu_devs[iommu_id])
+ iommu_devs[iommu_id]->ops->disable(iommu_devs[iommu_id]);
+ else
+ pr_err("%s, id:%d iommu don't exist, error!",
+ __func__, iommu_id);
+ }
+}
+
+static int sprd_iommu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int err = -1;
+ struct sprd_iommu_dev *iommu_dev = platform_get_drvdata(pdev);
+
+ pr_info("%s, id:%d, name:%s mmu_clock:0x%p mmu_mclock:0x%p suspend\n",
+ __func__, iommu_dev->init_data->id, iommu_dev->init_data->name,
+ iommu_dev->mmu_clock, iommu_dev->mmu_mclock);
+ err = iommu_dev->ops->backup(iommu_dev);
+
+ return err;
+}
+
+static int sprd_iommu_resume(struct platform_device *pdev)
+{
+ int err = -1;
+ struct sprd_iommu_dev *iommu_dev=platform_get_drvdata(pdev);
+
+ pr_info("%s, id:%d, name:%s mmu_clock:0x%p mmu_mclock:0x%p resume\n",
+ __func__, iommu_dev->init_data->id, iommu_dev->init_data->name,
+ iommu_dev->mmu_clock, iommu_dev->mmu_mclock);
+ err = iommu_dev->ops->restore(iommu_dev);
+
+ return err;
+}
+
+static inline int sprd_iommu_get_description(struct device_node *np,
+ char* desc_name, char**desc)
+{
+ return of_property_read_string(np, (const char*)desc_name,(const char**) desc);
+}
+
+static int sprd_iommu_get_resource(struct device_node *np,
+ struct sprd_iommu_init_data *pdata)
+{
+ int err = 0;
+ uint32_t val;
+ struct resource res;
+
+ pr_info("%s,name:%s\n", __func__, pdata->name);
+
+ err = of_address_to_resource(np, 0, &res);
+ if (err < 0)
+ return err;
+
+ pdata->iova_base = res.start;
+ pdata->iova_size = resource_size(&res);
+ pr_info("%s,iova_base:0x%lx,iova_size:%zx\n", __func__,
+ pdata->iova_base,pdata->iova_size);
+
+ err = of_address_to_resource(np, 1, &res);
+ if (err < 0)
+ return err;
+
+ pr_info("%s,pgt_base phy:0x%x\n", __func__, res.start);
+ pdata->pgt_base = (unsigned long)ioremap_nocache(res.start,
+ resource_size(&res));
+ if (!pdata->pgt_base)
+ BUG();
+
+ pdata->pgt_size = resource_size(&res);
+ pr_info("%s,pgt_base:%lx,pgt_size:%zx\n", __func__, pdata->pgt_base,
+ pdata->pgt_size);
+
+ err = of_address_to_resource(np, 2, &res);
+ if (err < 0)
+ return err;
+
+ pr_info("%s,ctrl_reg phy:0x%x\n", __func__, res.start);
+ pdata->ctrl_reg = (unsigned long)ioremap_nocache(res.start,
+ resource_size(&res));
+ if (!pdata->ctrl_reg)
+ BUG();
+ pr_info("%s,ctrl_reg:0x%lx\n", __func__, pdata->ctrl_reg);
+
+ err = of_property_read_u32(np, "sprd,reserved-fault-page", &val);
+ if (err) {
+ unsigned long page = 0;
+ uint32_t i;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (page) {
+ pdata->fault_page = virt_to_phys((void *)page);
+ } else {
+ pdata->fault_page = 0;
+ }
+ pr_info("%s, fault_page: 0x%lx\n", __func__, pdata->fault_page);
+ } else {
+ pr_info("%s, reserved fault page phy:0x%x\n", __func__, val);
+ pdata->fault_page = val;
+ }
+
+ err = of_property_read_u32(np, "sprd,reserved-rr-page", &val);
+ if (err) {
+ pr_info("%s, no reserved rr page addr\n", __func__);
+ pdata->re_route_page = 0;
+ } else {
+ pr_info("%s, reserved rr page phy:0x%x\n", __func__, val);
+ pdata->re_route_page = val;
+ }
+
+ err = of_property_read_u32(np, "sprd,iommu-rev", &val);
+ if (err) {
+ pr_info("%s, iommu-rev: r1\n", __func__);
+ pdata->iommu_rev = 1;
+ } else {
+ pr_info("%s, iommu-rev: %u\n", __func__, val);
+ pdata->iommu_rev = val;
+ }
+
+ return 0;
+}
+
+static int sprd_iommu_probe(struct platform_device *pdev)
+{
+ char func_name[] = "func-name";
+ char func_desc[128];
+ char* pdesc=&(func_desc[0]);
+ struct device_node *np = pdev->dev.of_node;
+ int err = 0;
+ struct sprd_iommu_init_data *pdata;
+ struct sprd_iommu_dev *iommu_dev;
+
+ pr_info("%s, begin\n", __func__);
+ iommu_dev = kzalloc(sizeof(struct sprd_iommu_dev), GFP_KERNEL);
+ if (NULL == iommu_dev) {
+ pr_err("%s, fail to kzalloc\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (-1 == sprd_iommu_get_description(np, func_name, &pdesc)) {
+ pr_err("%s, fail to get description\n", __func__);
+ err = -EINVAL;
+ goto errout;
+ }
+ pr_info("%s, function name is %s\n", __func__, pdesc);
+
+ if (0 == strncmp("sprd_iommu_gsp", pdesc, 14)) {
+ pdata = &sprd_iommu_gsp_data;
+ iommu_dev->ops = &iommu_gsp_ops;
+ } else if (0 == strncmp("sprd_iommu_mm", pdesc, 13)) {
+ pdata = &sprd_iommu_mm_data;
+ iommu_dev->ops = &iommu_mm_ops;
+ } else if (0 == strncmp("sprd_iommu_vsp", pdesc, 14)) {
+ pdata = &sprd_iommu_vsp_data;
+ iommu_dev->ops = &iommu_vsp_ops;
+ } else if (0 == strncmp("sprd_iommu_dcam", pdesc, 15)) {
+ pdata = &sprd_iommu_dcam_data;
+ iommu_dev->ops = &iommu_dcam_ops;
+ } else if (0 == strncmp("sprd_iommu_dispc", pdesc, 16)) {
+ pdata = &sprd_iommu_dispc_data;
+ iommu_dev->ops = &iommu_dispc_ops;
+ } else if (0 == strncmp("sprd_iommu_gsp0", pdesc, 15)) {
+ pdata = &sprd_iommu_gsp0_data;
+ iommu_dev->ops = &iommu_gsp0_ops;
+ } else if (0 == strncmp("sprd_iommu_gsp1", pdesc, 15)) {
+ pdata = &sprd_iommu_gsp1_data;
+ iommu_dev->ops = &iommu_gsp1_ops;
+ } else if (0 == strncmp("sprd_iommu_vpp", pdesc, 14)) {
+ pdata = &sprd_iommu_vpp_data;
+ iommu_dev->ops = &iommu_vpp_ops;
+ } else {
+ pr_err("%s, function name mismatch\n", __func__);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ err = sprd_iommu_get_resource(np, pdata);
+ if (err) {
+ dev_err(&pdev->dev, "no reg of property specified\n");
+ pr_err("%s: failed to get resource!\n", pdesc);
+ goto errout;
+ }
+
+ iommu_devs[pdata->id] = iommu_dev;
+ iommu_dev->init_data = pdata;
+ mutex_init(&iommu_dev->mutex_pgt);
+ mutex_init(&iommu_dev->mutex_map);
+ /*If ddr frequency >= 500Mz, iommu need enable div2 frequency,
+ * because of limit of iommu iram frq.*/
+ iommu_dev->div2_frq = 500;
+ iommu_dev->light_sleep_en = false;
+ iommu_dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
+ iommu_dev->misc_dev.name = pdata->name;
+ iommu_dev->misc_dev.fops = NULL;
+ iommu_dev->misc_dev.parent = NULL;
+
+ err = misc_register(&iommu_dev->misc_dev);
+ if (err) {
+ pr_err("iommu %s : failed to register misc device %d.\n",
+ pdata->name, err);
+ goto errout;
+ }
+
+ iommu_dev->misc_dev.this_device->of_node = pdev->dev.of_node;
+ err = iommu_dev->ops->init(iommu_dev, pdata);
+ if (err) {
+ pr_err("iommu %s : failed to init device %d.\n", pdata->name, err);
+ goto errout;
+ }
+
+ sprd_iommu_sysfs_register(iommu_dev, iommu_dev->init_data->name);
+ platform_set_drvdata(pdev, iommu_dev);
+
+ pr_info("%s, name:%s, dev->pgt:0x%lx, dev->pool:0x%p mmu_clock:0x%p\n",
+ __func__, pdata->name, iommu_dev->pgt,
+ iommu_dev->pool, iommu_dev->mmu_clock);
+ pr_info("%s, end\n", __func__);
+
+ return 0;
+
+errout:
+ iommu_devs[pdata->id] = NULL;
+ kfree(iommu_dev);
+ return err;
+}
+
+static int sprd_iommu_remove(struct platform_device *pdev)
+{
+ struct sprd_iommu_dev *iommu_dev=platform_get_drvdata(pdev);
+ misc_deregister(&iommu_dev->misc_dev);
+ sprd_iommu_sysfs_unregister(iommu_dev,iommu_dev->init_data->name);
+ iommu_dev->ops->exit(iommu_dev);
+ gen_pool_destroy(iommu_dev->pool);
+ kfree(iommu_dev);
+ return 0;
+}
+
+static int __init iommu_init(void)
+{
+ int err=-1;
+ pr_info("%s, begin\n", __func__);
+ err=platform_driver_register(&iommu_driver);
+ if (err < 0)
+ pr_err("sprd_iommu register err: %d\n",err);
+ pr_info("%s, end\n", __func__);
+ return err;
+}
+
+static void __exit iommu_exit(void)
+{
+ platform_driver_unregister(&iommu_driver);
+}
+
+module_init(iommu_init);
+module_exit(iommu_exit);
+
+MODULE_DESCRIPTION("SPRD IOMMU Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.c b/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.c
new file mode 100644
index 00000000..4bbc3061
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.c
@@ -0,0 +1,157 @@
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/genalloc.h>
+#include <linux/sprd_iommu.h>
+#include "sprd_iommu_sysfs.h"
+
+static struct dentry *iommu_debugfs_dir = NULL;
+extern int sprd_iommu_gsp_backup(struct sprd_iommu_dev *dev);
+extern int sprd_iommu_gsp_restore(struct sprd_iommu_dev *dev);
+extern int sprd_iommu_mm_backup(struct sprd_iommu_dev *dev);
+extern int sprd_iommu_mm_restore(struct sprd_iommu_dev *dev);
+
+static void iova_dump_chunk_bitmap(struct gen_pool *pool,
+ struct gen_pool_chunk *chunk, void *data)
+{
+ struct seq_file *s = (struct seq_file *)data;
+ unsigned long nbits = ((chunk->end_addr - chunk->start_addr) + (1UL << 12) - 1) >> 12;
+
+ seq_printf(s,"chunk phys_addr:0x%lx start_addr:0x%lx end_addr:0x%lx\n",
+ chunk->phys_addr, chunk->start_addr, chunk->end_addr);
+ seq_bitmap(s, chunk->bits, nbits);
+ seq_printf(s,"\n");
+}
+
+static int iova_show(struct seq_file *s, void *unused)
+{
+ struct sprd_iommu_dev *iommu_dev = (struct sprd_iommu_dev *)s->private;
+
+ seq_printf(s,"iommu_name:%s iova_base:0x%lx iova_size:0x%zx\n",
+ iommu_dev->init_data->name, iommu_dev->init_data->iova_base,
+ iommu_dev->init_data->iova_size);
+ gen_pool_for_each_chunk(iommu_dev->pool, iova_dump_chunk_bitmap, s);
+
+ return 0;
+}
+
+static int iova_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, iova_show, inode->i_private);
+}
+
+static const struct file_operations iova_fops = {
+ .owner = THIS_MODULE,
+ .open = iova_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int pgt_show(struct seq_file *s, void *unused)
+{
+ struct sprd_iommu_dev *iommu_dev = (struct sprd_iommu_dev *)s->private;
+ unsigned long i = 0;
+
+ seq_printf(s,"%s pgt_base:0x%lx pgt_size:0x%zx iova_base:0x%lx iova_size:0x%zx\n",
+ iommu_dev->init_data->name,
+ iommu_dev->init_data->pgt_base, iommu_dev->init_data->pgt_size,
+ iommu_dev->init_data->iova_base, iommu_dev->init_data->iova_size);
+
+ mutex_lock(&iommu_dev->mutex_map);
+ if (iommu_dev->light_sleep_en) {
+ if (0 == iommu_dev->map_count) {
+ iommu_dev->ops->enable(iommu_dev);
+ iommu_dev->ops->open(iommu_dev);
+ } else {
+ iommu_dev->ops->enable(iommu_dev);
+ }
+ } else {
+ if (0 == iommu_dev->map_count) {
+ iommu_dev->ops->enable(iommu_dev);
+ }
+ }
+
+ mutex_lock(&iommu_dev->mutex_pgt);
+ for (i = 0; i < (iommu_dev->init_data->pgt_size >> 2); i++)
+ {
+ if(i % 20 == 0)
+ {
+ seq_printf(s, "\n0x%lx[0x%lx]: ",
+ iommu_dev->init_data->pgt_base + i*4,
+ iommu_dev->init_data->iova_base + i*4096);
+ }
+ seq_printf(s, "%x,",
+ *(((uint32_t *)iommu_dev->init_data->pgt_base) + i));
+ }
+ mutex_unlock(&iommu_dev->mutex_pgt);
+
+ if (iommu_dev->light_sleep_en) {
+ if (0 == iommu_dev->map_count) {
+ iommu_dev->ops->release(iommu_dev);
+ iommu_dev->ops->disable(iommu_dev);
+ } else {
+ iommu_dev->ops->disable(iommu_dev);
+ }
+ } else {
+ if (0 == iommu_dev->map_count) {
+ iommu_dev->ops->disable(iommu_dev);
+ }
+ }
+ mutex_unlock(&iommu_dev->mutex_map);
+
+ return 0;
+}
+
+static int pgt_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pgt_show, inode->i_private);
+}
+
+static const struct file_operations pgt_fops = {
+ .owner = THIS_MODULE,
+ .open = pgt_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int sprd_iommu_sysfs_register(struct sprd_iommu_dev *device,
+ const char *dev_name)
+{
+ iommu_debugfs_dir = debugfs_create_dir(dev_name, NULL);
+
+ if(ERR_PTR(-ENODEV) == iommu_debugfs_dir)
+ {
+ /* Debugfs not supported. */
+ iommu_debugfs_dir = NULL;
+ }
+ else
+ {
+ if(NULL != iommu_debugfs_dir)
+ {
+ debugfs_create_file(
+ "iova", 0444, iommu_debugfs_dir, device, &iova_fops);
+ debugfs_create_file(
+ "pgtable", 0444, iommu_debugfs_dir, device, &pgt_fops);
+ }
+ }
+ return 0;
+}
+
+int sprd_iommu_sysfs_unregister(struct sprd_iommu_dev *device,
+ const char *dev_name)
+{
+ if(NULL != iommu_debugfs_dir)
+ {
+ debugfs_remove_recursive(iommu_debugfs_dir);
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.h b/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.h
new file mode 100644
index 00000000..dd8c40bb
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.h
@@ -0,0 +1,12 @@
+#ifndef __SPRD_IOMMU_SYSFS_H__
+#define __SPRD_IOMMU_SYSFS_H__
+
+#include <linux/device.h>
+
+int sprd_iommu_sysfs_register(struct sprd_iommu_dev *device,
+ const char *dev_name);
+
+int sprd_iommu_sysfs_unregister(struct sprd_iommu_dev *device,
+ const char *dev_name);
+
+#endif
diff --git a/drivers/gpu/sprd_iommu/v2/Makefile b/drivers/gpu/sprd_iommu/v2/Makefile
new file mode 100644
index 00000000..ea4985d6
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/v2/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_vsp.o
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_dcam.o
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_dispc.o
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_gsp0.o
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_gsp1.o
+obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_vpp.o
diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_dcam.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_dcam.c
new file mode 100644
index 00000000..273c930f
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_dcam.c
@@ -0,0 +1,192 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include"../sprd_iommu_common.h"
+
+void sprd_iommu_dcam_clk_enable(struct sprd_iommu_dev *dev)
+{
+ pr_info("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_prepare_enable(dev->mmu_mclock);
+ clk_prepare_enable(dev->mmu_clock);
+#endif
+}
+
+void sprd_iommu_dcam_clk_disable(struct sprd_iommu_dev *dev)
+{
+ pr_info("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_disable_unprepare(dev->mmu_clock);
+ clk_disable_unprepare(dev->mmu_mclock);
+#endif
+}
+
+void sprd_iommu_dcam_open(struct sprd_iommu_dev *dev)
+{
+}
+
+void sprd_iommu_dcam_release(struct sprd_iommu_dev *dev)
+{
+}
+
+int sprd_iommu_dcam_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int err = -1;
+#ifndef CONFIG_SC_FPGA
+ struct device_node *np;
+
+ np = dev->misc_dev.this_device->of_node;
+ if (!np)
+ return -1;
+
+ dev->mmu_clock = of_clk_get(np, 0);
+ dev->mmu_mclock = of_clk_get(np, 1);
+
+ if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) {
+ pr_info("%s, can't get clock:%p, %p\n", __func__,
+ dev->mmu_clock, dev->mmu_mclock);
+ goto errorout;
+ }
+#endif
+ sprd_iommu_dcam_clk_enable(dev);
+ err = sprd_iommu_init(dev, data);
+ sprd_iommu_dcam_clk_disable(dev);
+
+ return err;
+
+#ifndef CONFIG_SC_FPGA
+errorout:
+ if (dev->mmu_clock)
+ clk_put(dev->mmu_clock);
+
+ if (dev->mmu_mclock)
+ clk_put(dev->mmu_mclock);
+
+ return -1;
+#endif
+}
+
+int sprd_iommu_dcam_exit(struct sprd_iommu_dev *dev)
+{
+ int err = -1;
+
+ sprd_iommu_dcam_clk_enable(dev);
+ err = sprd_iommu_exit(dev);
+ sprd_iommu_dcam_clk_disable(dev);
+
+ return err;
+}
+
+unsigned long sprd_iommu_dcam_iova_alloc(struct sprd_iommu_dev *dev,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_alloc(dev, iova_length);
+}
+
+void sprd_iommu_dcam_iova_free(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_free(dev, iova, iova_length);
+}
+
+int sprd_iommu_dcam_iova_map(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length, struct sg_table *table)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count) {
+ sprd_iommu_dcam_clk_enable(dev);
+ sprd_iommu_reset_enable(dev);
+ }
+ dev->map_count++;
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_dcam_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+ dev->map_count--;
+ if (0 == dev->map_count) {
+ sprd_iommu_disable(dev);
+ sprd_iommu_dcam_clk_disable(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_dcam_backup(struct sprd_iommu_dev *dev)
+{
+ int err = 0;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+
+ if (dev->map_count > 0) {
+ err = sprd_iommu_backup(dev);
+ sprd_iommu_dcam_clk_disable(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_dcam_restore(struct sprd_iommu_dev *dev)
+{
+ int err = 0;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+
+ if (dev->map_count > 0) {
+ sprd_iommu_dcam_clk_enable(dev);
+ err = sprd_iommu_restore(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_dcam_dump(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_dump(dev, iova, iova_length);
+}
+
+struct sprd_iommu_ops iommu_dcam_ops = {
+ .init = sprd_iommu_dcam_init,
+ .exit = sprd_iommu_dcam_exit,
+ .iova_alloc = sprd_iommu_dcam_iova_alloc,
+ .iova_free = sprd_iommu_dcam_iova_free,
+ .iova_map = sprd_iommu_dcam_iova_map,
+ .iova_unmap = sprd_iommu_dcam_iova_unmap,
+ .backup = sprd_iommu_dcam_backup,
+ .restore = sprd_iommu_dcam_restore,
+ .enable = sprd_iommu_dcam_clk_enable,
+ .disable = sprd_iommu_dcam_clk_disable,
+ .dump = sprd_iommu_dcam_dump,
+ .open = sprd_iommu_dcam_open,
+ .release = sprd_iommu_dcam_release,
+};
+
diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_dispc.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_dispc.c
new file mode 100644
index 00000000..ac0ebd6a
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_dispc.c
@@ -0,0 +1,229 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include"../sprd_iommu_common.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void sprd_iommu_dispc_early_suspend(struct early_suspend *es)
+{
+ int err = -1;
+
+ struct sprd_iommu_dev *dev = container_of(es,
+ struct sprd_iommu_dev, early_suspend);
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_backup(dev);
+ mutex_unlock(&dev->mutex_map);
+}
+
+static void sprd_iommu_dispc_late_resume(struct early_suspend *es)
+{
+ int err = -1;
+
+ struct sprd_iommu_dev *dev = container_of(es,
+ struct sprd_iommu_dev, early_suspend);
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_restore(dev);
+ mutex_unlock(&dev->mutex_map);
+}
+#endif
+
+void sprd_iommu_dispc_clk_enable(struct sprd_iommu_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_prepare_enable(dev->mmu_mclock);
+ clk_prepare_enable(dev->mmu_clock);
+#endif
+}
+
+void sprd_iommu_dispc_clk_disable(struct sprd_iommu_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_disable_unprepare(dev->mmu_clock);
+ clk_disable_unprepare(dev->mmu_mclock);
+#endif
+}
+
+void sprd_iommu_dispc_open(struct sprd_iommu_dev *dev)
+{
+}
+
+void sprd_iommu_dispc_release(struct sprd_iommu_dev *dev)
+{
+}
+
+int sprd_iommu_dispc_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int err = -1;
+#ifndef CONFIG_SC_FPGA
+ struct device_node *np;
+
+ np = dev->misc_dev.this_device->of_node;
+ if (!np)
+ return -1;
+
+ dev->mmu_mclock = of_clk_get(np, 0);
+ dev->mmu_clock = of_clk_get(np, 2);
+
+ if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) {
+ pr_info("%s, can't get clock:%p, %p\n", __func__,
+ dev->mmu_clock, dev->mmu_mclock);
+ goto errorout;
+ }
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ dev->early_suspend.suspend = sprd_iommu_dispc_early_suspend;
+ dev->early_suspend.resume = sprd_iommu_dispc_late_resume;
+ dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING;
+ register_early_suspend(&dev->early_suspend);
+#endif
+
+ sprd_iommu_dispc_clk_enable(dev);
+ err = sprd_iommu_init(dev, data);
+ sprd_iommu_dispc_clk_disable(dev);
+
+ return err;
+
+#ifndef CONFIG_SC_FPGA
+errorout:
+ if (dev->mmu_clock)
+ clk_put(dev->mmu_clock);
+
+ if (dev->mmu_mclock)
+ clk_put(dev->mmu_mclock);
+
+ return -1;
+#endif
+}
+
+int sprd_iommu_dispc_exit(struct sprd_iommu_dev *dev)
+{
+ int err = -1;
+
+ sprd_iommu_dispc_clk_enable(dev);
+ err = sprd_iommu_exit(dev);
+ sprd_iommu_dispc_clk_disable(dev);
+
+ return err;
+}
+
+unsigned long sprd_iommu_dispc_iova_alloc(struct sprd_iommu_dev *dev,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_alloc(dev, iova_length);
+}
+
+void sprd_iommu_dispc_iova_free(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ sprd_iommu_iova_free(dev, iova, iova_length);
+}
+
+int sprd_iommu_dispc_iova_map(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length, struct sg_table *table)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count)
+ sprd_iommu_enable(dev);
+ dev->map_count++;
+
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_dispc_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+
+ dev->map_count--;
+ if (0 == dev->map_count)
+ sprd_iommu_disable(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_dispc_backup(struct sprd_iommu_dev *dev)
+{
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ return 0;
+#else
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_backup(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+#endif
+}
+
+int sprd_iommu_dispc_restore(struct sprd_iommu_dev *dev)
+{
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ return 0;
+#else
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_restore(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return 0;
+#endif
+}
+
+int sprd_iommu_dispc_dump(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_dump(dev, iova, iova_length);
+}
+
+struct sprd_iommu_ops iommu_dispc_ops = {
+ .init = sprd_iommu_dispc_init,
+ .exit = sprd_iommu_dispc_exit,
+ .iova_alloc = sprd_iommu_dispc_iova_alloc,
+ .iova_free = sprd_iommu_dispc_iova_free,
+ .iova_map = sprd_iommu_dispc_iova_map,
+ .iova_unmap = sprd_iommu_dispc_iova_unmap,
+ .backup = sprd_iommu_dispc_backup,
+ .restore = sprd_iommu_dispc_restore,
+ .enable = sprd_iommu_dispc_clk_enable,
+ .disable = sprd_iommu_dispc_clk_disable,
+ .dump = sprd_iommu_dispc_dump,
+ .open = sprd_iommu_dispc_open,
+ .release = sprd_iommu_dispc_release,
+};
+
diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp0.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp0.c
new file mode 100644
index 00000000..27a37502
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp0.c
@@ -0,0 +1,229 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include"../sprd_iommu_common.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void sprd_iommu_gsp0_early_suspend(struct early_suspend *es)
+{
+ int err = -1;
+
+ struct sprd_iommu_dev *dev = container_of(es,
+ struct sprd_iommu_dev, early_suspend);
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_backup(dev);
+ mutex_unlock(&dev->mutex_map);
+}
+
+static void sprd_iommu_gsp0_late_resume(struct early_suspend *es)
+{
+ int err = -1;
+
+ struct sprd_iommu_dev *dev = container_of(es,
+ struct sprd_iommu_dev, early_suspend);
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_restore(dev);
+ mutex_unlock(&dev->mutex_map);
+}
+#endif
+
+void sprd_iommu_gsp0_clk_enable(struct sprd_iommu_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_prepare_enable(dev->mmu_mclock);
+ clk_prepare_enable(dev->mmu_clock);
+#endif
+}
+
+void sprd_iommu_gsp0_clk_disable(struct sprd_iommu_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_disable_unprepare(dev->mmu_clock);
+ clk_disable_unprepare(dev->mmu_mclock);
+#endif
+}
+
+void sprd_iommu_gsp0_open(struct sprd_iommu_dev *dev)
+{
+}
+
+void sprd_iommu_gsp0_release(struct sprd_iommu_dev *dev)
+{
+}
+
+int sprd_iommu_gsp0_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int err = -1;
+#ifndef CONFIG_SC_FPGA
+ struct device_node *np;
+
+ np = dev->misc_dev.this_device->of_node;
+ if (!np)
+ return -1;
+
+ dev->mmu_mclock = of_clk_get(np, 0);
+ dev->mmu_clock = of_clk_get(np, 2);
+
+ if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) {
+ pr_info("%s, can't get clock:%p, %p\n", __func__,
+ dev->mmu_clock, dev->mmu_mclock);
+ goto errorout;
+ }
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ dev->early_suspend.suspend = sprd_iommu_gsp0_early_suspend;
+ dev->early_suspend.resume = sprd_iommu_gsp0_late_resume;
+ dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING;
+ register_early_suspend(&dev->early_suspend);
+#endif
+
+ sprd_iommu_gsp0_clk_enable(dev);
+ err = sprd_iommu_init(dev, data);
+ sprd_iommu_gsp0_clk_disable(dev);
+
+ return err;
+
+#ifndef CONFIG_SC_FPGA
+errorout:
+ if (dev->mmu_clock)
+ clk_put(dev->mmu_clock);
+
+ if (dev->mmu_mclock)
+ clk_put(dev->mmu_mclock);
+
+ return -1;
+#endif
+}
+
+int sprd_iommu_gsp0_exit(struct sprd_iommu_dev *dev)
+{
+ int err = -1;
+
+ sprd_iommu_gsp0_clk_enable(dev);
+ err = sprd_iommu_exit(dev);
+ sprd_iommu_gsp0_clk_disable(dev);
+
+ return err;
+}
+
+unsigned long sprd_iommu_gsp0_iova_alloc(struct sprd_iommu_dev *dev,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_alloc(dev, iova_length);
+}
+
+void sprd_iommu_gsp0_iova_free(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ sprd_iommu_iova_free(dev, iova, iova_length);
+}
+
+int sprd_iommu_gsp0_iova_map(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length, struct sg_table *table)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count)
+ sprd_iommu_enable(dev);
+ dev->map_count++;
+
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_gsp0_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+
+ dev->map_count--;
+ if (0 == dev->map_count)
+ sprd_iommu_disable(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_gsp0_backup(struct sprd_iommu_dev *dev)
+{
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ return 0;
+#else
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_backup(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+#endif
+}
+
+int sprd_iommu_gsp0_restore(struct sprd_iommu_dev *dev)
+{
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ return 0;
+#else
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_restore(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return 0;
+#endif
+}
+
+int sprd_iommu_gsp0_dump(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_dump(dev, iova, iova_length);
+}
+
+struct sprd_iommu_ops iommu_gsp0_ops = {
+ .init = sprd_iommu_gsp0_init,
+ .exit = sprd_iommu_gsp0_exit,
+ .iova_alloc = sprd_iommu_gsp0_iova_alloc,
+ .iova_free = sprd_iommu_gsp0_iova_free,
+ .iova_map = sprd_iommu_gsp0_iova_map,
+ .iova_unmap = sprd_iommu_gsp0_iova_unmap,
+ .backup = sprd_iommu_gsp0_backup,
+ .restore = sprd_iommu_gsp0_restore,
+ .enable = sprd_iommu_gsp0_clk_enable,
+ .disable = sprd_iommu_gsp0_clk_disable,
+ .dump = sprd_iommu_gsp0_dump,
+ .open = sprd_iommu_gsp0_open,
+ .release = sprd_iommu_gsp0_release,
+};
+
diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp1.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp1.c
new file mode 100644
index 00000000..3e3fb359
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp1.c
@@ -0,0 +1,229 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include"../sprd_iommu_common.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void sprd_iommu_gsp1_early_suspend(struct early_suspend *es)
+{
+ int err = -1;
+
+ struct sprd_iommu_dev *dev = container_of(es,
+ struct sprd_iommu_dev, early_suspend);
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_backup(dev);
+ mutex_unlock(&dev->mutex_map);
+}
+
+static void sprd_iommu_gsp1_late_resume(struct early_suspend *es)
+{
+ int err = -1;
+
+ struct sprd_iommu_dev *dev = container_of(es,
+ struct sprd_iommu_dev, early_suspend);
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_restore(dev);
+ mutex_unlock(&dev->mutex_map);
+}
+#endif
+
+void sprd_iommu_gsp1_clk_enable(struct sprd_iommu_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_prepare_enable(dev->mmu_mclock);
+ clk_prepare_enable(dev->mmu_clock);
+#endif
+}
+
+void sprd_iommu_gsp1_clk_disable(struct sprd_iommu_dev *dev)
+{
+ pr_debug("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_disable_unprepare(dev->mmu_clock);
+ clk_disable_unprepare(dev->mmu_mclock);
+#endif
+}
+
+void sprd_iommu_gsp1_open(struct sprd_iommu_dev *dev)
+{
+}
+
+void sprd_iommu_gsp1_release(struct sprd_iommu_dev *dev)
+{
+}
+
+int sprd_iommu_gsp1_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int err = -1;
+#ifndef CONFIG_SC_FPGA
+ struct device_node *np;
+
+ np = dev->misc_dev.this_device->of_node;
+ if (!np)
+ return -1;
+
+ dev->mmu_mclock = of_clk_get(np, 0);
+ dev->mmu_clock = of_clk_get(np, 2);
+
+ if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) {
+ pr_info("%s, can't get clock:%p, %p\n", __func__,
+ dev->mmu_clock, dev->mmu_mclock);
+ goto errorout;
+ }
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ dev->early_suspend.suspend = sprd_iommu_gsp1_early_suspend;
+ dev->early_suspend.resume = sprd_iommu_gsp1_late_resume;
+ dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING;
+ register_early_suspend(&dev->early_suspend);
+#endif
+
+ sprd_iommu_gsp1_clk_enable(dev);
+ err = sprd_iommu_init(dev, data);
+ sprd_iommu_gsp1_clk_disable(dev);
+
+ return err;
+
+#ifndef CONFIG_SC_FPGA
+errorout:
+ if (dev->mmu_clock)
+ clk_put(dev->mmu_clock);
+
+ if (dev->mmu_mclock)
+ clk_put(dev->mmu_mclock);
+
+ return -1;
+#endif
+}
+
+int sprd_iommu_gsp1_exit(struct sprd_iommu_dev *dev)
+{
+ int err = -1;
+
+ sprd_iommu_gsp1_clk_enable(dev);
+ err = sprd_iommu_exit(dev);
+ sprd_iommu_gsp1_clk_disable(dev);
+
+ return err;
+}
+
+unsigned long sprd_iommu_gsp1_iova_alloc(struct sprd_iommu_dev *dev,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_alloc(dev, iova_length);
+}
+
+void sprd_iommu_gsp1_iova_free(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ sprd_iommu_iova_free(dev, iova, iova_length);
+}
+
+int sprd_iommu_gsp1_iova_map(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length, struct sg_table *table)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count)
+ sprd_iommu_enable(dev);
+ dev->map_count++;
+
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_gsp1_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+
+ dev->map_count--;
+ if (0 == dev->map_count)
+ sprd_iommu_disable(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_gsp1_backup(struct sprd_iommu_dev *dev)
+{
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ return 0;
+#else
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_backup(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+#endif
+}
+
+int sprd_iommu_gsp1_restore(struct sprd_iommu_dev *dev)
+{
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ return 0;
+#else
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+ if (dev->map_count > 0)
+ err = sprd_iommu_restore(dev);
+ mutex_unlock(&dev->mutex_map);
+
+ return 0;
+#endif
+}
+
+int sprd_iommu_gsp1_dump(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_dump(dev, iova, iova_length);
+}
+
+struct sprd_iommu_ops iommu_gsp1_ops = {
+ .init = sprd_iommu_gsp1_init,
+ .exit = sprd_iommu_gsp1_exit,
+ .iova_alloc = sprd_iommu_gsp1_iova_alloc,
+ .iova_free = sprd_iommu_gsp1_iova_free,
+ .iova_map = sprd_iommu_gsp1_iova_map,
+ .iova_unmap = sprd_iommu_gsp1_iova_unmap,
+ .backup = sprd_iommu_gsp1_backup,
+ .restore = sprd_iommu_gsp1_restore,
+ .enable = sprd_iommu_gsp1_clk_enable,
+ .disable = sprd_iommu_gsp1_clk_disable,
+ .dump = sprd_iommu_gsp1_dump,
+ .open = sprd_iommu_gsp1_open,
+ .release = sprd_iommu_gsp1_release,
+};
+
diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_vpp.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_vpp.c
new file mode 100644
index 00000000..ba377e2f
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_vpp.c
@@ -0,0 +1,192 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include"../sprd_iommu_common.h"
+
+void sprd_iommu_vpp_clk_enable(struct sprd_iommu_dev *dev)
+{
+ pr_info("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_prepare_enable(dev->mmu_mclock);
+ clk_prepare_enable(dev->mmu_clock);
+#endif
+}
+
+void sprd_iommu_vpp_clk_disable(struct sprd_iommu_dev *dev)
+{
+ pr_info("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_disable_unprepare(dev->mmu_clock);
+ clk_disable_unprepare(dev->mmu_mclock);
+#endif
+}
+
+void sprd_iommu_vpp_open(struct sprd_iommu_dev *dev)
+{
+}
+
+void sprd_iommu_vpp_release(struct sprd_iommu_dev *dev)
+{
+}
+
+int sprd_iommu_vpp_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int err = -1;
+#ifndef CONFIG_SC_FPGA
+ struct device_node *np;
+
+ np = dev->misc_dev.this_device->of_node;
+ if (!np)
+ return -1;
+
+ dev->mmu_clock = of_clk_get(np, 0);
+ dev->mmu_mclock = of_clk_get(np, 1);
+
+ if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) {
+ pr_info("%s, can't get clock:%p, %p\n", __func__,
+ dev->mmu_clock, dev->mmu_mclock);
+ goto errorout;
+ }
+#endif
+ sprd_iommu_vpp_clk_enable(dev);
+ err = sprd_iommu_init(dev, data);
+ sprd_iommu_vpp_clk_disable(dev);
+
+ return err;
+
+#ifndef CONFIG_SC_FPGA
+errorout:
+ if (dev->mmu_clock)
+ clk_put(dev->mmu_clock);
+
+ if (dev->mmu_mclock)
+ clk_put(dev->mmu_mclock);
+
+ return -1;
+#endif
+}
+
+int sprd_iommu_vpp_exit(struct sprd_iommu_dev *dev)
+{
+ int err = -1;
+
+ sprd_iommu_vpp_clk_enable(dev);
+ err = sprd_iommu_exit(dev);
+ sprd_iommu_vpp_clk_disable(dev);
+
+ return err;
+}
+
+unsigned long sprd_iommu_vpp_iova_alloc(struct sprd_iommu_dev *dev,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_alloc(dev, iova_length);
+}
+
+void sprd_iommu_vpp_iova_free(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_free(dev, iova, iova_length);
+}
+
+int sprd_iommu_vpp_iova_map(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length, struct sg_table *table)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count) {
+ sprd_iommu_vpp_clk_enable(dev);
+ sprd_iommu_reset_enable(dev);
+ }
+ dev->map_count++;
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_vpp_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+ dev->map_count--;
+ if (0 == dev->map_count) {
+ sprd_iommu_disable(dev);
+ sprd_iommu_vpp_clk_disable(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_vpp_backup(struct sprd_iommu_dev *dev)
+{
+ int err = 0;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+
+ if (dev->map_count > 0) {
+ err = sprd_iommu_backup(dev);
+ sprd_iommu_vpp_clk_disable(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_vpp_restore(struct sprd_iommu_dev *dev)
+{
+ int err = 0;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+
+ if (dev->map_count > 0) {
+ sprd_iommu_vpp_clk_enable(dev);
+ err = sprd_iommu_restore(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_vpp_dump(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_dump(dev, iova, iova_length);
+}
+
+struct sprd_iommu_ops iommu_vpp_ops = {
+ .init = sprd_iommu_vpp_init,
+ .exit = sprd_iommu_vpp_exit,
+ .iova_alloc = sprd_iommu_vpp_iova_alloc,
+ .iova_free = sprd_iommu_vpp_iova_free,
+ .iova_map = sprd_iommu_vpp_iova_map,
+ .iova_unmap = sprd_iommu_vpp_iova_unmap,
+ .backup = sprd_iommu_vpp_backup,
+ .restore = sprd_iommu_vpp_restore,
+ .enable = sprd_iommu_vpp_clk_enable,
+ .disable = sprd_iommu_vpp_clk_disable,
+ .dump = sprd_iommu_vpp_dump,
+ .open = sprd_iommu_vpp_open,
+ .release = sprd_iommu_vpp_release,
+};
+
diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_vsp.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_vsp.c
new file mode 100644
index 00000000..2f1a28b9
--- /dev/null
+++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_vsp.c
@@ -0,0 +1,192 @@
+/*
+ * drivers/gpu/iommu/iommu.c *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include"../sprd_iommu_common.h"
+
+void sprd_iommu_vsp_clk_enable(struct sprd_iommu_dev *dev)
+{
+ pr_info("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_prepare_enable(dev->mmu_mclock);
+ clk_prepare_enable(dev->mmu_clock);
+#endif
+}
+
+void sprd_iommu_vsp_clk_disable(struct sprd_iommu_dev *dev)
+{
+ pr_info("%s\n", __func__);
+#ifndef CONFIG_SC_FPGA
+ clk_disable_unprepare(dev->mmu_clock);
+ clk_disable_unprepare(dev->mmu_mclock);
+#endif
+}
+
+void sprd_iommu_vsp_open(struct sprd_iommu_dev *dev)
+{
+}
+
+void sprd_iommu_vsp_release(struct sprd_iommu_dev *dev)
+{
+}
+
+int sprd_iommu_vsp_init(struct sprd_iommu_dev *dev,
+ struct sprd_iommu_init_data *data)
+{
+ int err = -1;
+#ifndef CONFIG_SC_FPGA
+ struct device_node *np;
+
+ np = dev->misc_dev.this_device->of_node;
+ if (!np)
+ return -1;
+
+ dev->mmu_clock = of_clk_get(np, 0);
+ dev->mmu_mclock = of_clk_get(np, 1);
+
+ if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) {
+ pr_info("%s, can't get clock:%p, %p\n", __func__,
+ dev->mmu_clock, dev->mmu_mclock);
+ goto errorout;
+ }
+#endif
+ sprd_iommu_vsp_clk_enable(dev);
+ err = sprd_iommu_init(dev, data);
+ sprd_iommu_vsp_clk_disable(dev);
+
+ return err;
+
+#ifndef CONFIG_SC_FPGA
+errorout:
+ if (dev->mmu_clock)
+ clk_put(dev->mmu_clock);
+
+ if (dev->mmu_mclock)
+ clk_put(dev->mmu_mclock);
+
+ return -1;
+#endif
+}
+
+int sprd_iommu_vsp_exit(struct sprd_iommu_dev *dev)
+{
+ int err = -1;
+
+ sprd_iommu_vsp_clk_enable(dev);
+ err = sprd_iommu_exit(dev);
+ sprd_iommu_vsp_clk_disable(dev);
+
+ return err;
+}
+
+unsigned long sprd_iommu_vsp_iova_alloc(struct sprd_iommu_dev *dev,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_alloc(dev, iova_length);
+}
+
+void sprd_iommu_vsp_iova_free(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_iova_free(dev, iova, iova_length);
+}
+
+int sprd_iommu_vsp_iova_map(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length, struct sg_table *table)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ if (0 == dev->map_count) {
+ sprd_iommu_vsp_clk_enable(dev);
+ sprd_iommu_reset_enable(dev);
+ }
+ dev->map_count++;
+ err = sprd_iommu_iova_map(dev, iova, iova_length, table);
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_vsp_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ int err = -1;
+
+ mutex_lock(&dev->mutex_map);
+ err = sprd_iommu_iova_unmap(dev, iova, iova_length);
+ dev->map_count--;
+ if (0 == dev->map_count) {
+ sprd_iommu_disable(dev);
+ sprd_iommu_vsp_clk_disable(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_vsp_backup(struct sprd_iommu_dev *dev)
+{
+ int err = 0;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+
+ if (dev->map_count > 0) {
+ err = sprd_iommu_backup(dev);
+ sprd_iommu_vsp_clk_disable(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_vsp_restore(struct sprd_iommu_dev *dev)
+{
+ int err = 0;
+
+ mutex_lock(&dev->mutex_map);
+ pr_info("%s, map_count:%d\n", __func__, dev->map_count);
+
+ if (dev->map_count > 0) {
+ sprd_iommu_vsp_clk_enable(dev);
+ err = sprd_iommu_restore(dev);
+ }
+ mutex_unlock(&dev->mutex_map);
+
+ return err;
+}
+
+int sprd_iommu_vsp_dump(struct sprd_iommu_dev *dev, unsigned long iova,
+ size_t iova_length)
+{
+ return sprd_iommu_dump(dev, iova, iova_length);
+}
+
+struct sprd_iommu_ops iommu_vsp_ops = {
+ .init = sprd_iommu_vsp_init,
+ .exit = sprd_iommu_vsp_exit,
+ .iova_alloc = sprd_iommu_vsp_iova_alloc,
+ .iova_free = sprd_iommu_vsp_iova_free,
+ .iova_map = sprd_iommu_vsp_iova_map,
+ .iova_unmap = sprd_iommu_vsp_iova_unmap,
+ .backup = sprd_iommu_vsp_backup,
+ .restore = sprd_iommu_vsp_restore,
+ .enable = sprd_iommu_vsp_clk_enable,
+ .disable = sprd_iommu_vsp_clk_disable,
+ .dump = sprd_iommu_vsp_dump,
+ .open = sprd_iommu_vsp_open,
+ .release = sprd_iommu_vsp_release,
+};
+