aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-12-05 14:44:20 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-12-05 14:44:20 -0800
commit75612d3929a4c52a5c39126b427865d6e583dff9 (patch)
treef66352e01255d2bb775542fb65e53676e9168470
parentc8c7af548d05efd37a5a7d14e2019317bca9bdf8 (diff)
downloadltsi-kernel-75612d3929a4c52a5c39126b427865d6e583dff9.tar.gz
add more armadillo800 patches
-rw-r--r--patches.armadillo800/iommu-shmobile-add-iommu-driver-for-renesas-ipmmu-modules.patch660
-rw-r--r--patches.armadillo800/shmobile-armadillo800eva-enable-iommu-support.patch90
-rw-r--r--patches.armadillo800/shmobile-armadillo800eva-set-lcd-color-depth-to-32-bpp.patch25
-rw-r--r--patches.armadillo800/shmobile-ipmmu-add-basic-pmb-support.patch676
-rw-r--r--patches.armadillo800/shmobile-r8a7740-add-meram-uio-device.patch64
-rw-r--r--patches.armadillo800/shmobile-r8a7740-add-support-for-vcp-1-vpu-vpc-vio-multimedia-ip.patch205
-rw-r--r--patches.armadillo800/shmobile-r8a7740-set-up-meram-address-range.patch76
7 files changed, 1796 insertions, 0 deletions
diff --git a/patches.armadillo800/iommu-shmobile-add-iommu-driver-for-renesas-ipmmu-modules.patch b/patches.armadillo800/iommu-shmobile-add-iommu-driver-for-renesas-ipmmu-modules.patch
new file mode 100644
index 0000000000000..06ca44126dade
--- /dev/null
+++ b/patches.armadillo800/iommu-shmobile-add-iommu-driver-for-renesas-ipmmu-modules.patch
@@ -0,0 +1,660 @@
+From dhobsong@igel.co.jp Wed Nov 21 23:30:36 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 16:28:26 +0900
+Subject: [PATCH 3/7] iommu/shmobile: Add iommu driver for Renesas IPMMU modules
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Hideki EIRAKU <hdk@igel.co.jp>
+Message-ID: <1353569310-16564-4-git-send-email-dhobsong@igel.co.jp>
+
+
+From: Hideki EIRAKU <hdk@igel.co.jp>
+
+This is the Renesas IPMMU driver and IOMMU API implementation.
+
+The IPMMU module supports the MMU function and the PMB function. The
+MMU function provides address translation by pagetable compatible with
+ARMv6. The PMB function provides address translation including
+tile-linear translation. This patch implements the MMU function.
+
+The iommu driver does not register a platform driver directly because:
+- the register space of the MMU function and the PMB function
+ have a common register (used for settings flush), so they should ideally
+ have a way to appropriately share this register.
+- the MMU function uses the IOMMU API while the PMB function does not.
+- the two functions may be used independently.
+
+Signed-off-by: Hideki EIRAKU <hdk@igel.co.jp>
+---
+ arch/arm/mach-shmobile/Kconfig | 6
+ arch/arm/mach-shmobile/Makefile | 3
+ arch/arm/mach-shmobile/include/mach/ipmmu.h | 16 +
+ arch/arm/mach-shmobile/ipmmu.c | 150 +++++++++++
+ drivers/iommu/Kconfig | 56 ++++
+ drivers/iommu/Makefile | 1
+ drivers/iommu/shmobile-iommu.c | 352 ++++++++++++++++++++++++++++
+ 7 files changed, 584 insertions(+)
+ create mode 100644 arch/arm/mach-shmobile/include/mach/ipmmu.h
+ create mode 100644 arch/arm/mach-shmobile/ipmmu.c
+ create mode 100644 drivers/iommu/shmobile-iommu.c
+
+--- a/arch/arm/mach-shmobile/Kconfig
++++ b/arch/arm/mach-shmobile/Kconfig
+@@ -175,6 +175,12 @@ endmenu
+ config SH_CLK_CPG
+ bool
+
++config SHMOBILE_IPMMU
++ bool
++
++config SHMOBILE_IPMMU_TLB
++ bool
++
+ source "drivers/sh/Kconfig"
+
+ endif
+--- a/arch/arm/mach-shmobile/Makefile
++++ b/arch/arm/mach-shmobile/Makefile
+@@ -56,3 +56,6 @@ obj-$(CONFIG_MACH_ARMADILLO800EVA) += bo
+ # Framework support
+ obj-$(CONFIG_SMP) += $(smp-y)
+ obj-$(CONFIG_GENERIC_GPIO) += $(pfc-y)
++
++# IPMMU/IPMMUI
++obj-$(CONFIG_SHMOBILE_IPMMU) += ipmmu.o
+--- /dev/null
++++ b/arch/arm/mach-shmobile/include/mach/ipmmu.h
+@@ -0,0 +1,16 @@
++#ifdef CONFIG_SHMOBILE_IPMMU_TLB
++void ipmmu_tlb_flush(struct device *ipmmu_dev);
++void ipmmu_tlb_set(struct device *ipmmu_dev, unsigned long phys, int size,
++ int asid);
++void ipmmu_add_device(struct device *dev);
++int ipmmu_iommu_init(struct device *dev);
++#else
++static inline void ipmmu_add_device(struct device *dev)
++{
++}
++
++static int ipmmu_iommu_init(struct device *dev)
++{
++ return -EINVAL;
++}
++#endif
+--- /dev/null
++++ b/arch/arm/mach-shmobile/ipmmu.c
+@@ -0,0 +1,150 @@
++/*
++ * IPMMU/IPMMUI
++ * Copyright (C) 2012 Hideki EIRAKU
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/err.h>
++#include <linux/export.h>
++#include <linux/slab.h>
++#include <mach/ipmmu.h>
++
++#define IMCTR1 0x000
++#define IMCTR2 0x004
++#define IMASID 0x010
++#define IMTTBR 0x014
++#define IMTTBCR 0x018
++
++#define IMCTR1_TLBEN (1 << 0)
++#define IMCTR1_FLUSH (1 << 1)
++
++struct ipmmu_priv {
++ void __iomem *ipmmu_base;
++ int tlb_enabled;
++ struct mutex flush_lock;
++};
++
++static void ipmmu_reg_write(struct ipmmu_priv *priv, unsigned long reg_off,
++ unsigned long data)
++{
++ iowrite32(data, priv->ipmmu_base + reg_off);
++}
++
++void ipmmu_tlb_flush(struct device *dev)
++{
++ struct ipmmu_priv *priv;
++
++ if (!dev)
++ return;
++ priv = dev_get_drvdata(dev);
++ mutex_lock(&priv->flush_lock);
++ if (priv->tlb_enabled)
++ ipmmu_reg_write(priv, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN);
++ else
++ ipmmu_reg_write(priv, IMCTR1, IMCTR1_FLUSH);
++ mutex_unlock(&priv->flush_lock);
++}
++
++void ipmmu_tlb_set(struct device *dev, unsigned long phys, int size, int asid)
++{
++ struct ipmmu_priv *priv;
++
++ if (!dev)
++ return;
++ priv = dev_get_drvdata(dev);
++ mutex_lock(&priv->flush_lock);
++ switch (size) {
++ default:
++ priv->tlb_enabled = 0;
++ break;
++ case 0x2000:
++ ipmmu_reg_write(priv, IMTTBCR, 1);
++ priv->tlb_enabled = 1;
++ break;
++ case 0x1000:
++ ipmmu_reg_write(priv, IMTTBCR, 2);
++ priv->tlb_enabled = 1;
++ break;
++ case 0x800:
++ ipmmu_reg_write(priv, IMTTBCR, 3);
++ priv->tlb_enabled = 1;
++ break;
++ case 0x400:
++ ipmmu_reg_write(priv, IMTTBCR, 4);
++ priv->tlb_enabled = 1;
++ break;
++ case 0x200:
++ ipmmu_reg_write(priv, IMTTBCR, 5);
++ priv->tlb_enabled = 1;
++ break;
++ case 0x100:
++ ipmmu_reg_write(priv, IMTTBCR, 6);
++ priv->tlb_enabled = 1;
++ break;
++ case 0x80:
++ ipmmu_reg_write(priv, IMTTBCR, 7);
++ priv->tlb_enabled = 1;
++ break;
++ }
++ ipmmu_reg_write(priv, IMTTBR, phys);
++ ipmmu_reg_write(priv, IMASID, asid);
++ mutex_unlock(&priv->flush_lock);
++}
++
++static int __devinit ipmmu_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++ struct ipmmu_priv *priv;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res) {
++ dev_err(&pdev->dev, "cannot get platform resources\n");
++ return -ENOENT;
++ }
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv) {
++ dev_err(&pdev->dev, "cannot allocate device data\n");
++ return -ENOMEM;
++ }
++ mutex_init(&priv->flush_lock);
++ priv->ipmmu_base = ioremap_nocache(res->start, resource_size(res));
++ if (!priv->ipmmu_base) {
++ dev_err(&pdev->dev, "ioremap_nocache failed\n");
++ kfree(priv);
++ return -ENOMEM;
++ }
++ platform_set_drvdata(pdev, priv);
++ ipmmu_reg_write(priv, IMCTR1, 0x0); /* disable TLB */
++ ipmmu_reg_write(priv, IMCTR2, 0x0); /* disable PMB */
++ ipmmu_iommu_init(&pdev->dev);
++ return 0;
++}
++
++static struct platform_driver ipmmu_driver = {
++ .probe = ipmmu_probe,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "ipmmu",
++ },
++};
++
++static int __init ipmmu_init(void)
++{
++ return platform_driver_register(&ipmmu_driver);
++}
++subsys_initcall(ipmmu_init);
+--- a/drivers/iommu/Kconfig
++++ b/drivers/iommu/Kconfig
+@@ -162,4 +162,60 @@ config TEGRA_IOMMU_SMMU
+ space through the SMMU (System Memory Management Unit)
+ hardware included on Tegra SoCs.
+
++config SHMOBILE_IOMMU
++ bool "IOMMU for Renesas IPMMU/IPMMUI"
++ default n
++ select IOMMU_API
++ select ARM_DMA_USE_IOMMU
++ select SHMOBILE_IPMMU
++ select SHMOBILE_IPMMU_TLB
++
++choice
++ prompt "IPMMU/IPMMUI address space size"
++ default SHMOBILE_IOMMU_ADDRSIZE_2048MB
++ depends on SHMOBILE_IOMMU
++ help
++ This option sets IPMMU/IPMMUI address space size by
++ adjusting the 1st level page table size. The page table size
++ is calculated as follows:
++
++ page table size = number of page table entries * 4 bytes
++ number of page table entries = address space size / 1 MiB
++
++ For example, when the address space size is 2048 MiB, the
++ 1st level page table size is 8192 bytes.
++
++ config SHMOBILE_IOMMU_ADDRSIZE_2048MB
++ bool "2 GiB"
++
++ config SHMOBILE_IOMMU_ADDRSIZE_1024MB
++ bool "1 GiB"
++
++ config SHMOBILE_IOMMU_ADDRSIZE_512MB
++ bool "512 MiB"
++
++ config SHMOBILE_IOMMU_ADDRSIZE_256MB
++ bool "256 MiB"
++
++ config SHMOBILE_IOMMU_ADDRSIZE_128MB
++ bool "128 MiB"
++
++ config SHMOBILE_IOMMU_ADDRSIZE_64MB
++ bool "64 MiB"
++
++ config SHMOBILE_IOMMU_ADDRSIZE_32MB
++ bool "32 MiB"
++
++endchoice
++
++config SHMOBILE_IOMMU_L1SIZE
++ int
++ default 8192 if SHMOBILE_IOMMU_ADDRSIZE_2048MB
++ default 4096 if SHMOBILE_IOMMU_ADDRSIZE_1024MB
++ default 2048 if SHMOBILE_IOMMU_ADDRSIZE_512MB
++ default 1024 if SHMOBILE_IOMMU_ADDRSIZE_256MB
++ default 512 if SHMOBILE_IOMMU_ADDRSIZE_128MB
++ default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB
++ default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB
++
+ endif # IOMMU_SUPPORT
+--- a/drivers/iommu/Makefile
++++ b/drivers/iommu/Makefile
+@@ -10,3 +10,4 @@ obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o
+ obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
+ obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
+ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
++obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
+--- /dev/null
++++ b/drivers/iommu/shmobile-iommu.c
+@@ -0,0 +1,352 @@
++/*
++ * IOMMU for IPMMU/IPMMUI
++ * Copyright (C) 2012 Hideki EIRAKU
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#include <linux/io.h>
++#include <linux/dmapool.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++#include <linux/iommu.h>
++#include <linux/dma-mapping.h>
++#include <mach/ipmmu.h>
++#include <asm/dma-iommu.h>
++
++#define L1_SIZE CONFIG_SHMOBILE_IOMMU_L1SIZE
++#define L1_LEN (L1_SIZE / 4)
++#define L1_ALIGN L1_SIZE
++#define L2_SIZE 0x400
++#define L2_LEN (L2_SIZE / 4)
++#define L2_ALIGN L2_SIZE
++
++struct shmobile_iommu_priv_pgtable {
++ uint32_t *pgtable;
++ dma_addr_t handle;
++};
++
++struct shmobile_iommu_priv {
++ struct shmobile_iommu_priv_pgtable l1, l2[L1_LEN];
++ spinlock_t map_lock;
++ atomic_t active;
++};
++
++static struct dma_iommu_mapping *iommu_mapping;
++static struct device *ipmmu_devices;
++static struct dma_pool *l1pool, *l2pool;
++static spinlock_t lock;
++static DEFINE_SPINLOCK(lock_add);
++static struct shmobile_iommu_priv *attached;
++static int num_attached_devices;
++static struct device *ipmmu_access_device;
++
++static int shmobile_iommu_domain_init(struct iommu_domain *domain)
++{
++ struct shmobile_iommu_priv *priv;
++ int i;
++
++ priv = kmalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++ priv->l1.pgtable = dma_pool_alloc(l1pool, GFP_KERNEL,
++ &priv->l1.handle);
++ if (!priv->l1.pgtable) {
++ kfree(priv);
++ return -ENOMEM;
++ }
++ for (i = 0; i < L1_LEN; i++)
++ priv->l2[i].pgtable = NULL;
++ memset(priv->l1.pgtable, 0, L1_SIZE);
++ spin_lock_init(&priv->map_lock);
++ atomic_set(&priv->active, 0);
++ domain->priv = priv;
++ return 0;
++}
++
++static void shmobile_iommu_domain_destroy(struct iommu_domain *domain)
++{
++ struct shmobile_iommu_priv *priv = domain->priv;
++ int i;
++
++ for (i = 0; i < L1_LEN; i++) {
++ if (priv->l2[i].pgtable)
++ dma_pool_free(l2pool, priv->l2[i].pgtable,
++ priv->l2[i].handle);
++ }
++ dma_pool_free(l1pool, priv->l1.pgtable, priv->l1.handle);
++ kfree(priv);
++ domain->priv = NULL;
++}
++
++static int shmobile_iommu_attach_device(struct iommu_domain *domain,
++ struct device *dev)
++{
++ struct shmobile_iommu_priv *priv = domain->priv;
++ int ret = -EBUSY;
++
++ spin_lock(&lock);
++ if (attached != priv) {
++ if (attached)
++ goto err;
++ atomic_set(&priv->active, 1);
++ ipmmu_tlb_set(ipmmu_access_device, priv->l1.handle, L1_SIZE,
++ 0);
++ wmb();
++ ipmmu_tlb_flush(ipmmu_access_device);
++ attached = priv;
++ num_attached_devices = 0;
++ }
++ num_attached_devices++;
++ ret = 0;
++err:
++ spin_unlock(&lock);
++ return ret;
++}
++
++static void shmobile_iommu_detach_device(struct iommu_domain *domain,
++ struct device *dev)
++{
++ struct shmobile_iommu_priv *priv = domain->priv;
++
++ spin_lock(&lock);
++ atomic_set(&priv->active, 0);
++ num_attached_devices--;
++ if (!num_attached_devices) {
++ ipmmu_tlb_set(ipmmu_access_device, 0, 0, 0);
++ ipmmu_tlb_flush(ipmmu_access_device);
++ attached = NULL;
++ }
++ spin_unlock(&lock);
++}
++
++static int
++l2alloc(struct shmobile_iommu_priv *priv, unsigned int l1index)
++{
++ if (!priv->l2[l1index].pgtable) {
++ priv->l2[l1index].pgtable = dma_pool_alloc(l2pool, GFP_KERNEL,
++ &priv->l2[l1index].handle);
++ if (!priv->l2[l1index].pgtable)
++ return -ENOMEM;
++ memset(priv->l2[l1index].pgtable, 0, L2_SIZE);
++ }
++ priv->l1.pgtable[l1index] = priv->l2[l1index].handle | 0x1;
++ return 0;
++}
++
++static void
++l2realfree(struct shmobile_iommu_priv_pgtable *l2)
++{
++ if (l2->pgtable)
++ dma_pool_free(l2pool, l2->pgtable, l2->handle);
++}
++
++static int
++l2free(struct shmobile_iommu_priv *priv, unsigned int l1index,
++ struct shmobile_iommu_priv_pgtable *l2)
++{
++ priv->l1.pgtable[l1index] = 0;
++ if (priv->l2[l1index].pgtable) {
++ *l2 = priv->l2[l1index];
++ priv->l2[l1index].pgtable = NULL;
++ }
++ return 0;
++}
++
++static int shmobile_iommu_map(struct iommu_domain *domain, unsigned long iova,
++ phys_addr_t paddr, size_t size, int prot)
++{
++ struct shmobile_iommu_priv_pgtable l2 = { .pgtable = NULL };
++ struct shmobile_iommu_priv *priv = domain->priv;
++ unsigned int l1index, l2index, i;
++ int ret;
++
++ l1index = iova >> 20;
++ switch (size) {
++ case 0x1000:
++ l2index = (iova >> 12) & 0xff;
++ spin_lock(&priv->map_lock);
++ ret = l2alloc(priv, l1index);
++ if (!ret)
++ priv->l2[l1index].pgtable[l2index] = paddr | 0xff2;
++ spin_unlock(&priv->map_lock);
++ break;
++ case 0x10000:
++ l2index = (iova >> 12) & 0xf0;
++ spin_lock(&priv->map_lock);
++ ret = l2alloc(priv, l1index);
++ if (!ret) {
++ for (i = 0; i < 0x10; i++)
++ priv->l2[l1index].pgtable[l2index + i] =
++ paddr | 0xff1;
++ }
++ spin_unlock(&priv->map_lock);
++ break;
++ case 0x100000:
++ spin_lock(&priv->map_lock);
++ l2free(priv, l1index, &l2);
++ priv->l1.pgtable[l1index] = paddr | 0xc02;
++ spin_unlock(&priv->map_lock);
++ ret = 0;
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ if (!ret && atomic_read(&priv->active)) {
++ wmb();
++ ipmmu_tlb_flush(ipmmu_access_device);
++ l2realfree(&l2);
++ }
++ return ret;
++}
++
++static size_t shmobile_iommu_unmap(struct iommu_domain *domain,
++ unsigned long iova, size_t size)
++{
++ struct shmobile_iommu_priv_pgtable l2 = { .pgtable = NULL };
++ struct shmobile_iommu_priv *priv = domain->priv;
++ unsigned int l1index, l2index, i;
++ uint32_t l2entry = 0;
++ size_t ret = 0;
++
++ l1index = iova >> 20;
++ if (!(iova & 0xFFFFF) && size >= 0x100000) {
++ spin_lock(&priv->map_lock);
++ l2free(priv, l1index, &l2);
++ spin_unlock(&priv->map_lock);
++ ret = 0x100000;
++ goto done;
++ }
++ l2index = (iova >> 12) & 0xff;
++ spin_lock(&priv->map_lock);
++ if (priv->l2[l1index].pgtable)
++ l2entry = priv->l2[l1index].pgtable[l2index];
++ switch (l2entry & 3) {
++ case 1:
++ if (l2index & 0xf)
++ break;
++ for (i = 0; i < 0x10; i++)
++ priv->l2[l1index].pgtable[l2index + i] = 0;
++ ret = 0x10000;
++ break;
++ case 2:
++ priv->l2[l1index].pgtable[l2index] = 0;
++ ret = 0x1000;
++ break;
++ }
++ spin_unlock(&priv->map_lock);
++done:
++ if (ret && atomic_read(&priv->active)) {
++ wmb();
++ ipmmu_tlb_flush(ipmmu_access_device);
++ l2realfree(&l2);
++ }
++ return ret;
++}
++
++static phys_addr_t shmobile_iommu_iova_to_phys(struct iommu_domain *domain,
++ unsigned long iova)
++{
++ struct shmobile_iommu_priv *priv = domain->priv;
++ uint32_t l1entry = 0, l2entry = 0;
++ unsigned int l1index, l2index;
++
++ l1index = iova >> 20;
++ l2index = (iova >> 12) & 0xff;
++ spin_lock(&priv->map_lock);
++ if (priv->l2[l1index].pgtable)
++ l2entry = priv->l2[l1index].pgtable[l2index];
++ else
++ l1entry = priv->l1.pgtable[l1index];
++ spin_unlock(&priv->map_lock);
++ switch (l2entry & 3) {
++ case 1:
++ return (l2entry & ~0xffff) | (iova & 0xffff);
++ case 2:
++ return (l2entry & ~0xfff) | (iova & 0xfff);
++ default:
++ if ((l1entry & 3) == 2)
++ return (l1entry & ~0xfffff) | (iova & 0xfffff);
++ return 0;
++ }
++}
++
++static struct iommu_ops shmobile_iommu_ops = {
++ .domain_init = shmobile_iommu_domain_init,
++ .domain_destroy = shmobile_iommu_domain_destroy,
++ .attach_dev = shmobile_iommu_attach_device,
++ .detach_dev = shmobile_iommu_detach_device,
++ .map = shmobile_iommu_map,
++ .unmap = shmobile_iommu_unmap,
++ .iova_to_phys = shmobile_iommu_iova_to_phys,
++ .pgsize_bitmap = 0x111000,
++};
++
++static int shmobile_iommu_attach_all_devices(void)
++{
++ struct device *dev;
++ int ret = 0;
++
++ spin_lock(&lock_add);
++ iommu_mapping = arm_iommu_create_mapping(&platform_bus_type, 0x0,
++ L1_LEN << 20, 0);
++ if (IS_ERR_OR_NULL(iommu_mapping)) {
++ ret = PTR_ERR(iommu_mapping);
++ goto err;
++ }
++ for (dev = ipmmu_devices; dev; dev = dev->archdata.iommu) {
++ if (arm_iommu_attach_device(dev, iommu_mapping))
++ pr_err("arm_iommu_attach_device failed\n");
++ }
++err:
++ spin_unlock(&lock_add);
++ return 0;
++}
++
++void ipmmu_add_device(struct device *dev)
++{
++ spin_lock(&lock_add);
++ dev->archdata.iommu = ipmmu_devices;
++ ipmmu_devices = dev;
++ if (!IS_ERR_OR_NULL(iommu_mapping)) {
++ if (arm_iommu_attach_device(dev, iommu_mapping))
++ pr_err("arm_iommu_attach_device failed\n");
++ }
++ spin_unlock(&lock_add);
++}
++
++int ipmmu_iommu_init(struct device *dev)
++{
++ dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
++ l1pool = dma_pool_create("shmobile-iommu-pgtable1", dev,
++ L1_SIZE, L1_ALIGN, 0);
++ if (!l1pool)
++ goto nomem_pool1;
++ l2pool = dma_pool_create("shmobile-iommu-pgtable2", dev,
++ L2_SIZE, L2_ALIGN, 0);
++ if (!l2pool)
++ goto nomem_pool2;
++ spin_lock_init(&lock);
++ attached = NULL;
++ ipmmu_access_device = dev;
++ bus_set_iommu(&platform_bus_type, &shmobile_iommu_ops);
++ if (shmobile_iommu_attach_all_devices())
++ pr_err("shmobile_iommu_attach_all_devices failed\n");
++ return 0;
++nomem_pool2:
++ dma_pool_destroy(l1pool);
++nomem_pool1:
++ return -ENOMEM;
++}
diff --git a/patches.armadillo800/shmobile-armadillo800eva-enable-iommu-support.patch b/patches.armadillo800/shmobile-armadillo800eva-enable-iommu-support.patch
new file mode 100644
index 0000000000000..95a54e889c618
--- /dev/null
+++ b/patches.armadillo800/shmobile-armadillo800eva-enable-iommu-support.patch
@@ -0,0 +1,90 @@
+From dhobsong@igel.co.jp Wed Nov 21 23:30:38 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 16:28:27 +0900
+Subject: [PATCH 4/7] shmobile: armadillo800eva enable IOMMU support
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353569310-16564-5-git-send-email-dhobsong@igel.co.jp>
+
+
+Add the IPMMU device and register the LCDC, VPU, and VIO devices to allocate
+DMA memory via the IPMMU.
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ arch/arm/mach-shmobile/board-armadillo800eva.c | 3 +++
+ arch/arm/mach-shmobile/setup-r8a7740.c | 23 +++++++++++++++++++++++
+ 2 files changed, 26 insertions(+)
+
+--- a/arch/arm/mach-shmobile/board-armadillo800eva.c
++++ b/arch/arm/mach-shmobile/board-armadillo800eva.c
+@@ -40,6 +40,7 @@
+ #include <mach/common.h>
+ #include <mach/irqs.h>
+ #include <mach/r8a7740.h>
++#include <mach/ipmmu.h>
+ #include <media/mt9t112.h>
+ #include <media/sh_mobile_ceu.h>
+ #include <media/soc_camera.h>
+@@ -1177,6 +1178,8 @@ static void __init eva_init(void)
+
+ r8a7740_add_standard_devices();
+
++ ipmmu_add_device(&lcdc0_device.dev);
++
+ platform_add_devices(eva_devices,
+ ARRAY_SIZE(eva_devices));
+
+--- a/arch/arm/mach-shmobile/setup-r8a7740.c
++++ b/arch/arm/mach-shmobile/setup-r8a7740.c
+@@ -35,6 +35,7 @@
+ #include <mach/r8a7740.h>
+ #include <mach/pm-rmobile.h>
+ #include <mach/common.h>
++#include <mach/ipmmu.h>
+ #include <mach/irqs.h>
+ #include <asm/mach-types.h>
+ #include <asm/mach/map.h>
+@@ -359,6 +360,23 @@ static struct platform_device cmt10_devi
+ .num_resources = ARRAY_SIZE(cmt10_resources),
+ };
+
++/* IPMMUI (an IPMMU module for ICB/LMB) */
++static struct resource ipmmu_resources[] = {
++ [0] = {
++ .name = "IPMMUI",
++ .start = 0xfe951000,
++ .end = 0xfe9510ff,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device ipmmu_device = {
++ .name = "ipmmu",
++ .id = -1,
++ .resource = ipmmu_resources,
++ .num_resources = ARRAY_SIZE(ipmmu_resources),
++};
++
+ static struct platform_device *r8a7740_early_devices[] __initdata = {
+ &scif0_device,
+ &scif1_device,
+@@ -373,6 +391,7 @@ static struct platform_device *r8a7740_e
+ &vpu_device,
+ &vpc_device,
+ &vio_device,
++ &ipmmu_device,
+
+ };
+
+@@ -778,6 +797,10 @@ void __init r8a7740_add_standard_devices
+ /* add devices */
+ platform_add_devices(r8a7740_early_devices,
+ ARRAY_SIZE(r8a7740_early_devices));
++
++ ipmmu_add_device(&vpu_device.dev);
++ ipmmu_add_device(&vio_device.dev);
++
+ platform_add_devices(r8a7740_late_devices,
+ ARRAY_SIZE(r8a7740_late_devices));
+
diff --git a/patches.armadillo800/shmobile-armadillo800eva-set-lcd-color-depth-to-32-bpp.patch b/patches.armadillo800/shmobile-armadillo800eva-set-lcd-color-depth-to-32-bpp.patch
new file mode 100644
index 0000000000000..b4a8fd02954b6
--- /dev/null
+++ b/patches.armadillo800/shmobile-armadillo800eva-set-lcd-color-depth-to-32-bpp.patch
@@ -0,0 +1,25 @@
+From dhobsong@igel.co.jp Wed Nov 21 23:30:33 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 16:28:25 +0900
+Subject: [PATCH 2/7] shmobile: armadillo800eva: Set LCD color depth to 32 bpp
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353569310-16564-3-git-send-email-dhobsong@igel.co.jp>
+
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ arch/arm/mach-shmobile/board-armadillo800eva.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/mach-shmobile/board-armadillo800eva.c
++++ b/arch/arm/mach-shmobile/board-armadillo800eva.c
+@@ -385,7 +385,7 @@ static struct sh_mobile_lcdc_info lcdc0_
+ .clock_source = LCDC_CLK_BUS,
+ .ch[0] = {
+ .chan = LCDC_CHAN_MAINLCD,
+- .fourcc = V4L2_PIX_FMT_RGB565,
++ .fourcc = V4L2_PIX_FMT_BGR32,
+ .interface_type = RGB24,
+ .clock_divider = 5,
+ .flags = 0,
diff --git a/patches.armadillo800/shmobile-ipmmu-add-basic-pmb-support.patch b/patches.armadillo800/shmobile-ipmmu-add-basic-pmb-support.patch
new file mode 100644
index 0000000000000..41a7ee086626e
--- /dev/null
+++ b/patches.armadillo800/shmobile-ipmmu-add-basic-pmb-support.patch
@@ -0,0 +1,676 @@
+From dhobsong@igel.co.jp Wed Nov 21 23:30:40 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 16:28:28 +0900
+Subject: [PATCH 5/7] shmobile: ipmmu: Add basic PMB support
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353569310-16564-6-git-send-email-dhobsong@igel.co.jp>
+
+
+Currently only the userspace API via character device is supported.
+All register access and hardware dependent functionality is
+provided by the IPMMU driver, which is shared with the IOMMU/TLB API.
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ arch/arm/mach-shmobile/Kconfig | 7
+ arch/arm/mach-shmobile/Makefile | 1
+ arch/arm/mach-shmobile/include/mach/ipmmu.h | 34 +++
+ arch/arm/mach-shmobile/ipmmu-pmb.c | 269 ++++++++++++++++++++++++++++
+ arch/arm/mach-shmobile/ipmmu.c | 236 +++++++++++++++++++++++-
+ include/linux/ipmmu.h | 29 +++
+ 6 files changed, 565 insertions(+), 11 deletions(-)
+ create mode 100644 arch/arm/mach-shmobile/ipmmu-pmb.c
+ create mode 100644 include/linux/ipmmu.h
+
+--- a/arch/arm/mach-shmobile/Kconfig
++++ b/arch/arm/mach-shmobile/Kconfig
+@@ -181,6 +181,13 @@ config SHMOBILE_IPMMU
+ config SHMOBILE_IPMMU_TLB
+ bool
+
++config SHMOBILE_PMB
++ bool "IPMMU PMB driver"
++ default n
++ select SHMOBILE_IPMMU
++ help
++ This enables build of the IPMMU PMB driver.
++
+ source "drivers/sh/Kconfig"
+
+ endif
+--- a/arch/arm/mach-shmobile/Makefile
++++ b/arch/arm/mach-shmobile/Makefile
+@@ -59,3 +59,4 @@ obj-$(CONFIG_GENERIC_GPIO) += $(pfc-y)
+
+ # IPMMU/IPMMUI
+ obj-$(CONFIG_SHMOBILE_IPMMU) += ipmmu.o
++obj-$(CONFIG_SHMOBILE_PMB) += ipmmu-pmb.o
+--- a/arch/arm/mach-shmobile/include/mach/ipmmu.h
++++ b/arch/arm/mach-shmobile/include/mach/ipmmu.h
+@@ -1,5 +1,12 @@
+-#ifdef CONFIG_SHMOBILE_IPMMU_TLB
++#include <linux/ipmmu.h>
++#ifndef __SHMOBILE_IPMMU_H__
++#define __SHMOBILE_IPMMU_H__
++
++#ifdef CONFIG_SHMOBILE_IPMMU
+ void ipmmu_tlb_flush(struct device *ipmmu_dev);
++#endif
++
++#ifdef CONFIG_SHMOBILE_IPMMU_TLB
+ void ipmmu_tlb_set(struct device *ipmmu_dev, unsigned long phys, int size,
+ int asid);
+ void ipmmu_add_device(struct device *dev);
+@@ -9,8 +16,31 @@ static inline void ipmmu_add_device(stru
+ {
+ }
+
+-static int ipmmu_iommu_init(struct device *dev)
++static inline int ipmmu_iommu_init(struct device *dev)
+ {
+ return -EINVAL;
+ }
+ #endif
++#ifdef CONFIG_SHMOBILE_PMB
++/* Access functions used by PMB device */
++void handle_free(struct device *dev, unsigned long handle, int size_mb);
++unsigned long handle_alloc(struct device *dev, int size_mb);
++int ipmmu_pmb_set_addr(struct device *dev, int index, unsigned long addr,
++ int enabled);
++int ipmmu_pmb_set_data(struct device *dev, int index,
++ struct ipmmu_pmb_info *info,
++ struct pmb_tile_info *tile);
++int ipmmu_pmb_enable(struct device *dev, int index);
++/* PMB initialization */
++void *ipmmu_pmb_init(struct device *dev);
++void ipmmu_pmb_deinit(void *pmb_priv);
++#else
++static inline void *ipmmu_pmb_init(struct device *dev)
++{
++ return NULL;
++}
++static inline void ipmmu_pmb_deinit(void *pmb_priv)
++{
++}
++#endif
++#endif /* __SHMOBILE_IPMMU_H__ */
+--- /dev/null
++++ b/arch/arm/mach-shmobile/ipmmu-pmb.c
+@@ -0,0 +1,269 @@
++/*
++ * IPMMU-PMB driver
++ * Copyright (C) 2012 Damian Hobson-Garcia
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/err.h>
++#include <linux/export.h>
++#include <linux/cdev.h>
++#include <linux/fs.h>
++#include <linux/slab.h>
++#include <linux/ipmmu.h>
++#include <mach/ipmmu.h>
++#include <asm/uaccess.h>
++
++#define PMB_DEVICE_NAME "pmb"
++
++#define PMB_NR 16
++struct ipmmu_pmb_data {
++ struct ipmmu_pmb_priv *priv;
++ struct ipmmu_pmb_info info;
++ struct pmb_tile_info tile;
++ unsigned long handle;
++ int index;
++};
++
++struct ipmmu_pmb_priv {
++ struct cdev cdev;
++ struct class *pmb_class;
++ dev_t pmb_dev;
++ unsigned long busy_pmbs;
++ struct mutex pmb_lock;
++ struct ipmmu_pmb_data pmbs[PMB_NR];
++ struct device *ipmmu_dev;
++ int pmb_enabled;
++};
++
++struct ipmmu_pmb_priv *static_priv;
++
++static int set_pmb(struct ipmmu_pmb_data *data,
++ struct ipmmu_pmb_info *info)
++{
++ struct ipmmu_pmb_priv *priv = data->priv;
++ unsigned long data_mask;
++ int err;
++
++ if (!info->enabled) {
++ if (data->handle) {
++ handle_free(priv->ipmmu_dev, data->handle,
++ data->info.size_mb);
++ data->handle = 0;
++ }
++ data->info = *info;
++ ipmmu_pmb_set_data(priv->ipmmu_dev, data->index, NULL, NULL);
++ ipmmu_pmb_set_addr(priv->ipmmu_dev, data->index, 0, 0);
++ ipmmu_tlb_flush(priv->ipmmu_dev);
++ return 0;
++ }
++
++ if (data->info.enabled) {
++ err = -EBUSY;
++ goto err_out;
++ }
++
++ data_mask = ~((info->size_mb) - 1);
++
++ if (info->paddr & ~(data_mask)) {
++ err = -EINVAL;
++ goto err_out;
++ }
++
++ data->handle = handle_alloc(priv->ipmmu_dev, info->size_mb);
++
++ if (!data->handle) {
++ err = -ENOMEM;
++ goto err_out;
++ }
++
++ data->info = *info;
++
++ ipmmu_pmb_set_addr(priv->ipmmu_dev, data->index, data->handle, 1);
++ ipmmu_pmb_set_data(priv->ipmmu_dev, data->index, &data->info,
++ &data->tile);
++
++ if (!data->priv->pmb_enabled) {
++ ipmmu_pmb_enable(priv->ipmmu_dev, 1);
++ data->priv->pmb_enabled = 1;
++ }
++
++ ipmmu_tlb_flush(priv->ipmmu_dev);
++
++ return 0;
++
++err_out:
++ info->enabled = 0;
++ return err;
++}
++
++static int set_tile(struct ipmmu_pmb_data *data,
++ struct pmb_tile_info *tile)
++{
++ struct ipmmu_pmb_priv *priv = data->priv;
++ data->tile = *tile;
++ return ipmmu_pmb_set_data(priv->ipmmu_dev, data->index, &data->info,
++ &data->tile);
++}
++
++static int ipmmu_pmb_open(struct inode *inode, struct file *filp)
++{
++ struct ipmmu_pmb_priv *priv;
++ int idx;
++ priv = container_of(inode->i_cdev, struct ipmmu_pmb_priv,
++ cdev);
++
++ mutex_lock(&priv->pmb_lock);
++ idx = find_first_zero_bit(&priv->busy_pmbs, PMB_NR);
++ if (idx == PMB_NR)
++ return -EBUSY;
++
++ __set_bit(idx, &priv->busy_pmbs);
++ mutex_unlock(&priv->pmb_lock);
++ priv->pmbs[idx].index = idx;
++ priv->pmbs[idx].priv = priv;
++ filp->private_data = &priv->pmbs[idx];
++ return 0;
++}
++
++static int ipmmu_pmb_release(struct inode *inode, struct file *filp)
++{
++ struct ipmmu_pmb_data *pmb;
++ pmb = filp->private_data;
++ if (pmb->info.enabled) {
++ pmb->info.enabled = 0;
++ set_pmb(pmb, &pmb->info);
++ }
++
++ mutex_lock(&pmb->priv->pmb_lock);
++ __clear_bit(pmb->index, &pmb->priv->busy_pmbs);
++ mutex_unlock(&pmb->priv->pmb_lock);
++ return 0;
++}
++
++static long ipmmu_pmb_ioctl(struct file *filp, unsigned int cmd_in,
++ unsigned long arg)
++{
++ struct ipmmu_pmb_data *pmb;
++ struct ipmmu_pmb_info user_set;
++ struct pmb_tile_info user_tile;
++ long ret = -EINVAL;
++ pmb = filp->private_data;
++ switch (cmd_in) {
++ case IPMMU_GET_PMB:
++ ret = copy_to_user((char *)arg, &pmb->info, sizeof(pmb->info));
++ break;
++ case IPMMU_SET_PMB:
++ ret = copy_from_user(&user_set, (char *)arg, sizeof(user_set));
++ if (ret)
++ break;
++ ret = set_pmb(pmb, &user_set);
++ if (!ret)
++ pmb->info = user_set;
++ break;
++ case IPMMU_GET_PMB_HANDLE:
++ ret = copy_to_user((char *)arg, &pmb->handle,
++ sizeof(pmb->handle));
++ break;
++ case IPMMU_GET_PMB_TL:
++ ret = copy_to_user((char *)arg, &pmb->tile, sizeof(pmb->tile));
++ break;
++ case IPMMU_SET_PMB_TL:
++ ret = copy_from_user(&user_tile, (char *)arg,
++ sizeof(user_tile));
++ if (ret)
++ break;
++ ret = set_tile(pmb, &user_tile);
++ if (!ret)
++ pmb->tile = user_tile;
++ break;
++ }
++ return ret;
++}
++
++static const struct file_operations ipmmu_pmb_fops = {
++ .owner = THIS_MODULE,
++ .open = ipmmu_pmb_open,
++ .release = ipmmu_pmb_release,
++ .unlocked_ioctl = ipmmu_pmb_ioctl,
++};
++
++void ipmmu_pmb_deinit(void *arg)
++{
++ struct ipmmu_pmb_priv *priv = arg;
++
++ if (!priv || IS_ERR(priv))
++ return;
++
++ cdev_del(&priv->cdev);
++ device_destroy(priv->pmb_class, priv->pmb_dev);
++ unregister_chrdev_region(priv->pmb_dev, 1);
++ class_destroy(priv->pmb_class);
++ kfree(priv);
++}
++
++void *ipmmu_pmb_init(struct device *ipmmu_dev)
++{
++ int err = -ENOENT;
++ struct ipmmu_pmb_priv *priv;
++
++ priv = kzalloc(sizeof(struct ipmmu_pmb_priv), GFP_KERNEL);
++ if (!priv) {
++ dev_err(ipmmu_dev, "cannot allocate device data\n");
++ return ERR_PTR(-ENOMEM);
++ }
++
++ priv->ipmmu_dev = ipmmu_dev;
++ static_priv = priv;
++
++ mutex_init(&priv->pmb_lock);
++
++ priv->pmb_class = class_create(THIS_MODULE, "ipmmu-pmb");
++ if (!priv->pmb_class)
++ goto free_priv;
++
++ err = alloc_chrdev_region(&priv->pmb_dev, 0, 1, PMB_DEVICE_NAME);
++ if (err) {
++ dev_err(ipmmu_dev, "cannot allocate device num\n");
++ goto destroy_class;
++ }
++
++ if (!device_create(priv->pmb_class, ipmmu_dev, priv->pmb_dev, priv,
++ "pmb"))
++ goto unregister_region;
++
++ cdev_init(&priv->cdev, &ipmmu_pmb_fops);
++ priv->cdev.owner = THIS_MODULE;
++ priv->cdev.ops = &ipmmu_pmb_fops;
++ err = cdev_add(&priv->cdev, priv->pmb_dev, 1);
++ if (err) {
++ dev_err(ipmmu_dev, "cannot add ipmmu_pmb device\n");
++ goto destroy_device;
++ }
++
++ return priv;
++
++destroy_device:
++ device_destroy(priv->pmb_class, priv->pmb_dev);
++unregister_region:
++ unregister_chrdev_region(priv->pmb_dev, 1);
++destroy_class:
++ class_destroy(priv->pmb_class);
++free_priv:
++ kfree(priv);
++ return ERR_PTR(err);
++}
+--- a/arch/arm/mach-shmobile/ipmmu.c
++++ b/arch/arm/mach-shmobile/ipmmu.c
+@@ -23,28 +23,242 @@
+ #include <linux/export.h>
+ #include <linux/slab.h>
+ #include <mach/ipmmu.h>
++#include <linux/ipmmu.h>
+
+-#define IMCTR1 0x000
+-#define IMCTR2 0x004
+-#define IMASID 0x010
+-#define IMTTBR 0x014
+-#define IMTTBCR 0x018
+-
++#define IMCTR1 0x00
+ #define IMCTR1_TLBEN (1 << 0)
+ #define IMCTR1_FLUSH (1 << 1)
++#define IMCTR2 0x04
++#define IMCTR2_PMBEN 0x01
++#define IMASID 0x010
++#define IMTTBR 0x014
++#define IMTTBCR 0x018
++#define IMPMBA_BASE 0x80
++#define IMPBMA_V (1 << 8)
++#define IMPMBD_BASE 0xC0
++#define IMPBMD_V (1 << 8)
++#define IMPBMD_SZ_16M 0x00
++#define IMPBMD_SZ_64M 0x10
++#define IMPBMD_SZ_128M 0x80
++#define IMPBMD_SZ_512M 0x90
++#define IMPBMD_BV (1 << 9)
++#define IMPBMD_TBM_MASK (7 << 12)
++#define IMPBMD_TBM_POS 12
++#define IMPBMD_HBM_MASK (7 << 16)
++#define IMPBMD_HBM_POS 16
++#define IMPBMD_VBM_MASK (7 << 20)
++#define IMPBMD_VBM_POS 20
++
++#define IMPBMA(x) (IMPMBA_BASE + 0x4*x)
++#define IMPBMD(x) (IMPMBD_BASE + 0x4*x)
++
++/* the smallest size that can be reserverd in the pmb */
++#define PMB_GRANULARITY (16 << 20)
++#define PMB_START_ADDR 0x80000000
++#define PMB_SIZE 0x40000000
++#define NUM_BITS(x) ((x) / PMB_GRANULARITY)
++#define NR_BITMAPS ((NUM_BITS(PMB_SIZE) + BITS_PER_LONG - 1) \
++ >> ilog2(BITS_PER_LONG))
+
+ struct ipmmu_priv {
+- void __iomem *ipmmu_base;
+- int tlb_enabled;
+- struct mutex flush_lock;
++ struct device *dev;
++ void __iomem *ipmmu_base;
++ int tlb_enabled;
++ struct mutex flush_lock;
++ struct mutex alloc_lock;
++ unsigned long alloc_bitmaps[NR_BITMAPS];
++ void *pmb_priv;
+ };
+
++static int valid_size(int size_mb)
++{
++ switch (size_mb) {
++ case 16:
++ case 64:
++ case 128:
++ case 512:
++ return 1;
++ }
++ return 0;
++
++}
++unsigned long handle_alloc(struct device *dev,
++ int size_mb)
++{
++ int i;
++ int idx;
++ unsigned long tmp_bitmap;
++ unsigned long alloc_mask;
++ unsigned long align_mask;
++ int alloc_bits;
++ struct ipmmu_priv *priv;
++
++ if (!valid_size(size_mb))
++ return -1;
++
++ priv = dev_get_drvdata(dev);
++
++ alloc_bits = NUM_BITS(size_mb << 20);
++ alloc_mask = alloc_bits < BITS_PER_LONG ?
++ (1 << alloc_bits) - 1 : -1;
++
++
++ align_mask = alloc_mask - 1;
++ for (i = BITS_PER_LONG >> 1; i >= alloc_bits; i = i >> 1)
++ align_mask = align_mask | (align_mask << i);
++
++ mutex_lock(&priv->alloc_lock);
++ for (i = 0; i < NR_BITMAPS; i++) {
++ tmp_bitmap = priv->alloc_bitmaps[i];
++ tmp_bitmap |= align_mask;
++ idx = 0;
++ while (idx < BITS_PER_LONG) {
++ idx = find_next_zero_bit(&tmp_bitmap, BITS_PER_LONG,
++ idx);
++ if (!((alloc_mask << idx) & priv->alloc_bitmaps[i]) ||
++ (idx == BITS_PER_LONG))
++ break;
++ idx++;
++ }
++ if (idx < BITS_PER_LONG)
++ break;
++ }
++ if (i == NR_BITMAPS) {
++ mutex_unlock(&priv->alloc_lock);
++ return 0;
++ }
++
++ priv->alloc_bitmaps[i] |= (alloc_mask << idx);
++ mutex_unlock(&priv->alloc_lock);
++
++ return PMB_START_ADDR + (i * BITS_PER_LONG + idx) * PMB_GRANULARITY;
++}
++
++void handle_free(struct device *dev,
++ unsigned long handle,
++ int size_mb)
++{
++ int idx;
++ unsigned long offset;
++ unsigned long alloc_bits;
++ unsigned long alloc_mask;
++ struct ipmmu_priv *priv;
++
++ priv = dev_get_drvdata(dev);
++
++ alloc_bits = NUM_BITS(size_mb << 20);
++ alloc_mask = alloc_bits < BITS_PER_LONG ?
++ (1 << alloc_bits) - 1 : -1;
++ offset = handle - PMB_START_ADDR;
++ offset /= PMB_GRANULARITY;
++ idx = offset & (BITS_PER_LONG - 1);
++ offset = offset / BITS_PER_LONG;
++ mutex_lock(&priv->alloc_lock);
++ priv->alloc_bitmaps[offset] &= ~(alloc_mask << idx);
++ mutex_unlock(&priv->alloc_lock);
++}
++
+ static void ipmmu_reg_write(struct ipmmu_priv *priv, unsigned long reg_off,
+ unsigned long data)
+ {
+ iowrite32(data, priv->ipmmu_base + reg_off);
+ }
+
++int ipmmu_pmb_enable(struct device *dev,
++ int enable)
++{
++
++ struct ipmmu_priv *priv;
++
++ priv = dev_get_drvdata(dev);
++ ipmmu_reg_write(priv, IMCTR2, enable ? IMCTR2_PMBEN : 0);
++ return 0;
++
++}
++int ipmmu_pmb_set_addr(struct device *dev,
++ int index,
++ unsigned long addr,
++ int enabled)
++{
++
++ struct ipmmu_priv *priv;
++
++ priv = dev_get_drvdata(dev);
++ if (!enabled) {
++ ipmmu_reg_write(priv, IMPBMA(index), 0);
++ return 0;
++ }
++
++ ipmmu_reg_write(priv, IMPBMA(index), addr |
++ IMPBMD_V);
++ return 0;
++
++}
++
++int ipmmu_pmb_set_data(struct device *dev,
++ int index,
++ struct ipmmu_pmb_info *info,
++ struct pmb_tile_info *tile)
++{
++ int vbm, hbm, tbm;
++ int w, h;
++ unsigned long temp;
++ struct ipmmu_priv *priv;
++
++ priv = dev_get_drvdata(dev);
++
++ if (!info || !info->enabled) {
++ ipmmu_reg_write(priv, IMPBMD(index), 0);
++ return 0;
++ }
++
++ temp = info->paddr;
++
++ switch (info->size_mb) {
++ case 16:
++ temp |= IMPBMD_SZ_16M;
++ break;
++ case 64:
++ temp |= IMPBMD_SZ_64M;
++ break;
++ case 128:
++ temp |= IMPBMD_SZ_128M;
++ break;
++ case 512:
++ temp |= IMPBMD_SZ_512M;
++ break;
++ default:
++ break;
++ }
++
++ temp |= IMPBMD_V;
++
++ if (!tile || !tile->enabled) {
++ ipmmu_reg_write(priv, IMPBMD(index), temp);
++ return 0;
++ }
++
++ w = tile->tile_width;
++ h = tile->tile_height;
++
++ if (w & (w - 1) || h & (h - 1))
++ return -EINVAL;
++
++ tbm = ilog2(tile->tile_width);
++ vbm = ilog2(tile->tile_height) - 1;
++ hbm = ilog2(tile->buffer_pitch) - tbm - 1;
++ tbm -= 4;
++
++ temp |= (tbm << IMPBMD_TBM_POS) & IMPBMD_TBM_MASK;
++ temp |= (vbm << IMPBMD_VBM_POS) & IMPBMD_VBM_MASK;
++ temp |= (hbm << IMPBMD_HBM_POS) & IMPBMD_HBM_MASK;
++ temp |= IMPBMD_BV;
++ ipmmu_reg_write(priv, IMPBMD(index),
++ temp);
++ ipmmu_tlb_flush(priv->dev);
++ return 0;
++}
++
+ void ipmmu_tlb_flush(struct device *dev)
+ {
+ struct ipmmu_priv *priv;
+@@ -122,6 +336,8 @@ static int __devinit ipmmu_probe(struct
+ return -ENOMEM;
+ }
+ mutex_init(&priv->flush_lock);
++ mutex_init(&priv->alloc_lock);
++
+ priv->ipmmu_base = ioremap_nocache(res->start, resource_size(res));
+ if (!priv->ipmmu_base) {
+ dev_err(&pdev->dev, "ioremap_nocache failed\n");
+@@ -129,9 +345,11 @@ static int __devinit ipmmu_probe(struct
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, priv);
++ priv->dev = &pdev->dev;
+ ipmmu_reg_write(priv, IMCTR1, 0x0); /* disable TLB */
+ ipmmu_reg_write(priv, IMCTR2, 0x0); /* disable PMB */
+ ipmmu_iommu_init(&pdev->dev);
++ priv->pmb_priv = ipmmu_pmb_init(&pdev->dev);
+ return 0;
+ }
+
+--- /dev/null
++++ b/include/linux/ipmmu.h
+@@ -0,0 +1,29 @@
++#ifndef __LINUX_IPMMU_PMB_H__
++#define __LINUX_IPMMU_PMB_H__
++
++struct ipmmu_pmb_info {
++ int enabled;
++ unsigned long paddr;
++ int size_mb;
++};
++
++struct pmb_tile_info {
++ int tile_width;
++ int tile_height;
++ int buffer_pitch;
++ int enabled;
++};
++
++/* IOCTL commands. */
++
++#define IPMMU_SET_PMB _IOW('S', 37, struct ipmmu_pmb_phys *)
++#define IPMMU_GET_PMB _IOR('S', 38, struct ipmmu_pmb_phys *)
++#define IPMMU_GET_PMB_HANDLE _IOR('S', 39, unsigned long *)
++#define IPMMU_SET_PMB_TL _IOW('S', 41, struct ipmmu_pmb_tile_info *)
++#define IPMMU_GET_PMB_TL _IOR('S', 42, struct ipmmu_pmb_tile_info *)
++
++#ifdef __kernel
++
++#endif /* __kernel */
++
++#endif /* __LINUX_IPMMU_PMB_H__ */
diff --git a/patches.armadillo800/shmobile-r8a7740-add-meram-uio-device.patch b/patches.armadillo800/shmobile-r8a7740-add-meram-uio-device.patch
new file mode 100644
index 0000000000000..401ef797d66e3
--- /dev/null
+++ b/patches.armadillo800/shmobile-r8a7740-add-meram-uio-device.patch
@@ -0,0 +1,64 @@
+From dhobsong@igel.co.jp Wed Nov 21 23:30:45 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 16:28:30 +0900
+Subject: [PATCH 7/7] shmobile: r8a7740: Add MERAM UIO device
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353569310-16564-8-git-send-email-dhobsong@igel.co.jp>
+
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ arch/arm/mach-shmobile/setup-r8a7740.c | 34 +++++++++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+--- a/arch/arm/mach-shmobile/setup-r8a7740.c
++++ b/arch/arm/mach-shmobile/setup-r8a7740.c
+@@ -105,6 +105,39 @@ static struct platform_device vpc_device
+ .num_resources = ARRAY_SIZE(vpc_resources),
+ };
+
++/* MERAM */
++static struct uio_info meram_uio_platform_data = {
++ .name = "MERAM",
++ .version = "0",
++ .irq = -1,
++};
++
++static struct resource meram_uio_resources[] = {
++ [0] = {
++ .name = "MERAM",
++ .start = 0xe8000000,
++ .end = 0xe807ffff,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .name = "MERAM_MEM",
++ .start = 0xe8080000,
++ .end = 0xe81fffff,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device meram_uio_device = {
++ .name = "uio_pdrv_genirq",
++ .id = 0,
++ .dev = {
++ .platform_data = &meram_uio_platform_data,
++ .coherent_dma_mask = ~0,
++ },
++ .resource = meram_uio_resources,
++ .num_resources = ARRAY_SIZE(meram_uio_resources),
++};
++
+ /* VCP1 */
+ static unsigned int regions[] = {
+ (80 << 20),
+@@ -424,6 +457,7 @@ static struct platform_device *r8a7740_e
+ &vio_device,
+ &ipmmu_device,
+ &meram_device,
++ &meram_uio_device,
+
+ };
+
diff --git a/patches.armadillo800/shmobile-r8a7740-add-support-for-vcp-1-vpu-vpc-vio-multimedia-ip.patch b/patches.armadillo800/shmobile-r8a7740-add-support-for-vcp-1-vpu-vpc-vio-multimedia-ip.patch
new file mode 100644
index 0000000000000..cf034f39aa4f6
--- /dev/null
+++ b/patches.armadillo800/shmobile-r8a7740-add-support-for-vcp-1-vpu-vpc-vio-multimedia-ip.patch
@@ -0,0 +1,205 @@
+From dhobsong@igel.co.jp Wed Nov 21 23:30:31 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 16:28:24 +0900
+Subject: [PATCH 1/7] shmobile: r8a7740: Add support for VCP-1 (VPU), VPC, VIO multimedia IP
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353569310-16564-2-git-send-email-dhobsong@igel.co.jp>
+
+
+Att the UIO device and clock settings for these multimedia IP cores
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ arch/arm/mach-shmobile/board-armadillo800eva.c | 1
+ arch/arm/mach-shmobile/clock-r8a7740.c | 9 ++
+ arch/arm/mach-shmobile/include/mach/common.h | 1
+ arch/arm/mach-shmobile/setup-r8a7740.c | 100 +++++++++++++++++++++++++
+ 4 files changed, 110 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/mach-shmobile/board-armadillo800eva.c
++++ b/arch/arm/mach-shmobile/board-armadillo800eva.c
+@@ -1212,6 +1212,7 @@ DT_MACHINE_START(ARMADILLO800EVA_DT, "ar
+ .init_early = eva_add_early_devices,
+ .init_irq = r8a7740_init_irq,
+ .handle_irq = shmobile_handle_irq_intc,
++ .reserve = r8a7740_reserve_memory,
+ .init_machine = eva_init,
+ .init_late = shmobile_init_late,
+ .timer = &shmobile_timer,
+--- a/arch/arm/mach-shmobile/clock-r8a7740.c
++++ b/arch/arm/mach-shmobile/clock-r8a7740.c
+@@ -459,7 +459,7 @@ static struct clk div6_clks[DIV6_NR] = {
+
+ enum {
+ MSTP128, MSTP127, MSTP125,
+- MSTP116, MSTP111, MSTP100, MSTP117,
++ MSTP116, MSTP111, MSTP100, MSTP117, MSTP101, MSTP107, MSTP103,
+
+ MSTP230,
+ MSTP222,
+@@ -483,6 +483,9 @@ static struct clk mstp_clks[MSTP_NR] = {
+ [MSTP116] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 16, 0), /* IIC0 */
+ [MSTP111] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 11, 0), /* TMU1 */
+ [MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */
++ [MSTP101] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 1, 0), /* VPU5HA2 */
++ [MSTP103] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 3, 0), /* VIO6C */
++ [MSTP107] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 7, 0), /* VPC */
+
+ [MSTP230] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 30, 0), /* SCIFA6 */
+ [MSTP222] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 22, 0), /* SCIFA7 */
+@@ -554,6 +557,10 @@ static struct clk_lookup lookups[] = {
+
+ /* MSTP32 clocks */
+ CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[MSTP100]),
++ CLKDEV_DEV_ID("uio_dmem_genirq.0", &mstp_clks[MSTP101]),
++ CLKDEV_DEV_ID("uio_pdrv_genirq.1", &mstp_clks[MSTP103]),
++ CLKDEV_DEV_ID("uio_pdrv_genirq.2", &mstp_clks[MSTP107]),
++ CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[MSTP100]),
+ CLKDEV_DEV_ID("sh_tmu.1", &mstp_clks[MSTP111]),
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP116]),
+ CLKDEV_DEV_ID("sh_mobile_lcdc_fb.1", &mstp_clks[MSTP117]),
+--- a/arch/arm/mach-shmobile/include/mach/common.h
++++ b/arch/arm/mach-shmobile/include/mach/common.h
+@@ -68,6 +68,7 @@ extern void r8a7740_map_io(void);
+ extern void r8a7740_add_early_devices(void);
+ extern void r8a7740_add_standard_devices(void);
+ extern void r8a7740_clock_init(u8 md_ck);
++extern void r8a7740_reserve_memory(void);
+ extern void r8a7740_pinmux_init(void);
+
+ extern void r8a7779_init_irq(void);
+--- a/arch/arm/mach-shmobile/setup-r8a7740.c
++++ b/arch/arm/mach-shmobile/setup-r8a7740.c
+@@ -23,10 +23,13 @@
+ #include <linux/init.h>
+ #include <linux/io.h>
+ #include <linux/platform_device.h>
++#include <linux/platform_data/uio_dmem_genirq.h>
+ #include <linux/of_platform.h>
+ #include <linux/serial_sci.h>
+ #include <linux/sh_dma.h>
+ #include <linux/sh_timer.h>
++#include <linux/uio_driver.h>
++#include <linux/dma-contiguous.h>
+ #include <linux/dma-mapping.h>
+ #include <mach/dma-register.h>
+ #include <mach/r8a7740.h>
+@@ -74,6 +77,94 @@ void __init r8a7740_map_io(void)
+ init_consistent_dma_size(12 << 20);
+ }
+
++/* VPC */
++static struct uio_info vpc_platform_data = {
++ .name = "VPC",
++ .version = "0",
++ .irq = -1,
++};
++
++static struct resource vpc_resources[] = {
++ [0] = {
++ .name = "VPC",
++ .start = 0xfe9d0000,
++ .end = 0xfe9d0020,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device vpc_device = {
++ .name = "uio_pdrv_genirq",
++ .id = 2,
++ .dev = {
++ .platform_data = &vpc_platform_data,
++ },
++ .resource = vpc_resources,
++ .num_resources = ARRAY_SIZE(vpc_resources),
++};
++
++/* VCP1 */
++static unsigned int regions[] = {
++ (80 << 20),
++};
++
++static struct uio_dmem_genirq_pdata vpu_platform_data = {
++ .uioinfo = {
++ .name = "VPU5",
++ .version = "0",
++ .irq = intcs_evt2irq(0x880),
++ },
++ .dynamic_region_sizes = regions,
++ .num_dynamic_regions = ARRAY_SIZE(regions),
++};
++
++static struct resource vpu_resources[] = {
++ [0] = {
++ .name = "VPU",
++ .start = 0xfe900000,
++ .end = 0xfe900157,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device vpu_device = {
++ .name = "uio_dmem_genirq",
++ .id = 0,
++ .dev = {
++ .platform_data = &vpu_platform_data,
++ .coherent_dma_mask = ~0,
++ },
++ .resource = vpu_resources,
++ .num_resources = ARRAY_SIZE(vpu_resources),
++};
++
++/* VI00 */
++static struct uio_info vio_platform_data = {
++ .name = "VIO6C",
++ .version = "0",
++ .irq = intcs_evt2irq(0x4E0),
++};
++
++static struct resource vio_resources[] = {
++ [0] = {
++ .name = "VIO6",
++ .start = 0xfe920000,
++ .end = 0xfe928000,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device vio_device = {
++ .name = "uio_pdrv_genirq",
++ .id = 1,
++ .dev = {
++ .platform_data = &vio_platform_data,
++ .coherent_dma_mask = ~0,
++ },
++ .resource = vio_resources,
++ .num_resources = ARRAY_SIZE(vio_resources),
++};
++
+ /* SCIFA0 */
+ static struct plat_sci_port scif0_platform_data = {
+ .mapbase = 0xe6c40000,
+@@ -279,6 +370,10 @@ static struct platform_device *r8a7740_e
+ &scif7_device,
+ &scifb_device,
+ &cmt10_device,
++ &vpu_device,
++ &vpc_device,
++ &vio_device,
++
+ };
+
+ /* DMA */
+@@ -718,6 +813,11 @@ void __init r8a7740_add_early_devices(vo
+ shmobile_timer.init = r8a7740_earlytimer_init;
+ }
+
++void __init r8a7740_reserve_memory(void)
++{
++ dma_declare_contiguous(&vpu_device.dev, 80 << 20, 0, 0);
++}
++
+ #ifdef CONFIG_USE_OF
+
+ void __init r8a7740_add_early_devices_dt(void)
diff --git a/patches.armadillo800/shmobile-r8a7740-set-up-meram-address-range.patch b/patches.armadillo800/shmobile-r8a7740-set-up-meram-address-range.patch
new file mode 100644
index 0000000000000..123a2b6278099
--- /dev/null
+++ b/patches.armadillo800/shmobile-r8a7740-set-up-meram-address-range.patch
@@ -0,0 +1,76 @@
+From dhobsong@igel.co.jp Wed Nov 21 23:30:42 2012
+From: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Date: Thu, 22 Nov 2012 16:28:29 +0900
+Subject: [PATCH 6/7] shmobile: r8a7740: Set up MERAM address range
+To: gregkh@linuxfoundation.org
+Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp>
+Message-ID: <1353569310-16564-7-git-send-email-dhobsong@igel.co.jp>
+
+
+The default address range settings have both the MERAM and the IPMMU-PMB
+set to use the same range, starting from 0x80000000. The MERAM has
+priority over this address range, which means that regardless of the PMB
+setting, any H/W accesses to the 0x80000000 memory region will be intercepted
+by the MERAM. This patch uses the MERAM driver to change the MERAM memory
+range to start at 0xC0000000.
+
+Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
+---
+ arch/arm/mach-shmobile/setup-r8a7740.c | 32 ++++++++++++++++++++++++++++++++
+ 1 file changed, 32 insertions(+)
+
+--- a/arch/arm/mach-shmobile/setup-r8a7740.c
++++ b/arch/arm/mach-shmobile/setup-r8a7740.c
+@@ -30,6 +30,7 @@
+ #include <linux/sh_timer.h>
+ #include <linux/uio_driver.h>
+ #include <linux/dma-contiguous.h>
++#include <video/sh_mobile_meram.h>
+ #include <linux/dma-mapping.h>
+ #include <mach/dma-register.h>
+ #include <mach/r8a7740.h>
+@@ -377,6 +378,36 @@ static struct platform_device ipmmu_devi
+ .num_resources = ARRAY_SIZE(ipmmu_resources),
+ };
+
++/* MERAM */
++static struct sh_mobile_meram_info meram_info = {
++ .addr_mode = SH_MOBILE_MERAM_MODE1,
++};
++
++static struct resource meram_resources[] = {
++ [0] = {
++ .name = "MERAM",
++ .start = 0xe8000000,
++ .end = 0xe807ffff,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .name = "MERAM",
++ .start = 0xe8080000,
++ .end = 0xe81fffff,
++ .flags = IORESOURCE_MEM,
++ }
++};
++
++static struct platform_device meram_device = {
++ .name = "sh_mobile_meram",
++ .id = 0,
++ .num_resources = ARRAY_SIZE(meram_resources),
++ .resource = meram_resources,
++ .dev = {
++ .platform_data = &meram_info,
++ },
++};
++
+ static struct platform_device *r8a7740_early_devices[] __initdata = {
+ &scif0_device,
+ &scif1_device,
+@@ -392,6 +423,7 @@ static struct platform_device *r8a7740_e
+ &vpc_device,
+ &vio_device,
+ &ipmmu_device,
++ &meram_device,
+
+ };
+